//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
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:    HWCTXT.CPP

Abstract:        Platform dependent code for the mixing audio driver.

Notes:            The following file contains all the hardware specific code
                for the mixing audio driver.  This code's primary responsibilities
                are:

                    * Initialize audio hardware (including codec chip)
                    * Schedule DMA operations (move data from/to buffers)
                    * Handle audio interrupts

                All other tasks (mixing, volume control, etc.) are handled by the "upper"
                layers of this driver.


-*/
#include "wavemain.h"
#include <ceddk.h>
#include <bsp_cfg.h>
#include <register_map.h>

#include <drvmsg.h>
#include <msgqueue.h>
#include <bsp_args.h>

#include <i2c_wave.h>
#include <WaveDevMsg.h>
#include <cregedit.h>

#define CLK_OUT_XUSBXTI  ((0x1 << 16) | (0x1 << 13))


// WM8580 Codec-chip Initialization Value 
unsigned int WM8580_Codec_Init_Table[][2] =\
{
     //master mclk=12MHz fs=44.1k
   //2450
  	{0x00,0x00},//R0 software reset 
   	{0x03,0xed},//R1 power management 1--PLL 0ff 
	{0x05,0x80},//R2 power management 2
	{0x06,0x6F},//R3 power management 3
	{0x08,0x10},//R4 audio interface control -- 16BIT  i2s
	{0x0d,0x4c},//R6 clock generation control //   0b1:master    0b0:slave
	{0x48,0x07},//R36 PLL N value
  	{0x4A,0x21},//R37 PLL K value 1 [23:20]
  	{0x4d,0x61},//R38 PLL K value 2 [17:9]
  	{0x4E,0x26},//R39 PLL K value 3 [8:0]
	//{0x4A,0x86},//R37 PLL K value 1 [23:20]
  	//{0x4d,0xc2},//R38 PLL K value 2 [17:9]
  	//{0x4E,0x26},//R39 PLL K value 3 [8:0]
  	
	{0x56,0x10},//R43 beep control 
  	{0x64,0x01},//R50 left channel output mixer control
	{0x66,0X01},//R51 right channel output mixer control 
	
	//{0x05,0x80},//R2 power management 2
	
	{0x69,0x39},//R52 LOUT1 volume control 
  	{0x6B,0x39},//R53 ROUT1 volume control 
  	{0x6D,0x3f},//R54 LOUT2 volume control (spk)
  	{0x6F,0x3f},//R55 ROUT2 volume control (spk)
};

//open SPK(out2) and out1 only
// ÇÐ»»µ½À®°ÈºÍ¶ú»ú
unsigned int WM8580_Codec_OpenOUT1_OUT2_Table[][2] =
{
	{104,0x1f},//R52(0x34) unmute out1l
  	{106,0x1f},//R53 unmute out1r 
  	{108,0x07/*0x3f*/},//R54 unmute out2l,vol max. lqm changed vol.10-12-01
  	{110,0x07/*0x3F*/},//R55 unmute out2r,vol max
  	{112,0x5f},//{112,0x07},//R56 mute out3
  	{114,0x5f},//R57   mute out4

	
};

//open SPK and out4 only
unsigned int WM8580_Codec_OpenOUT2_OUT4_Table[][2] =
{
	{104,0x40},//R52 mute out1l
  	{106,0x40},//R53 mute out1r 
  	{108,0x3f},//R54 unmute out2l,vol max
  	{110,0x3F},//R55 unmute out2r,vol max
  	{112,0x47},//R56  mute out3
  	{114,0x1f},//R57   unmute out4
};

typedef enum
{
    DMA_CH_OUT    = 0x1,
    DMA_CH_IN     = 0x2
} DMA_CH_SELECT;

#if 1 //WM8976  
//modified by terry 20110929 for WM8976 

	 
#ifdef WAV_I2S0
 #define I2C_CODEC_GROUP_NAME    L"I2C2:"	 // modified by terry for easy210 I2C2 20120313
#else //WAV_I2S1
 #define I2C_CODEC_GROUP_NAME    L"I2C1:"
#endif
#define I2C_CODEC_ID            0x34
#define I2C_CODEC_SPEED         100

#else

#define I2C_CODEC_ID            0x36
#define I2C_CODEC_SPEED         100
#define I2C_CODEC_GROUP_NAME    L"I2C0:"

#endif


#if 0//WM8976 //form 6410

#define CHIP_ID                  (0x34)        // WM8580 Chip ID
#define WM8580_READ                (CHIP_ID + 1)
#define WM8580_WRITE            (CHIP_ID + 0)
#endif


PHardwareContext g_pHWContext = NULL;
PHardwareContext HardwareContext::pBaseClass = NULL;

/*----------------------------------------------------------------------------
*Function: CreateHWContext

*Parameters: Index

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::CreateHWContext(DWORD Index)
{
    if (g_pHWContext)
    {
        return(TRUE);
    }

    g_pHWContext = new HardwareContext;
    if (g_pHWContext == NULL)
    {
        return(FALSE);
    }

    return(g_pHWContext->Initialize(Index));
}


/*----------------------------------------------------------------------------
*Function: HardwareContext

*Parameters: None

*Return Value: None

*Implementation Notes: Constructor
-----------------------------------------------------------------------------*/
HardwareContext::HardwareContext()
: m_InputDeviceContext(), m_OutputDeviceContext()
{
    InitializeCriticalSection(&m_csLock);
    m_bInitialized = FALSE;

    m_pIISReg = NULL;
    m_pASSClkReg = NULL;
    m_pASSCommBoxReg = NULL;
    m_pGPIOReg = NULL;
    m_pSysConReg = NULL;
    m_pPMUMiscReg = NULL;
}


/*----------------------------------------------------------------------------
*Function: HardwareContext

*Parameters: None

*Return Value: None

*Implementation Notes: Destructor
-----------------------------------------------------------------------------*/
HardwareContext::~HardwareContext()
{
    DeleteCriticalSection(&m_csLock);
}


/*----------------------------------------------------------------------------
*Function: Initialize

*Parameters: Index

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::Initialize(DWORD Index)
{
    MSGQUEUEOPTIONS msgOptions;
    CRegistryEdit cRegActive((LPCTSTR)Index);
    CRegistryEdit cRegSoftware(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\MediaPlayer\\Preferences");
    CRegistryEdit cRegSystem(HKEY_LOCAL_MACHINE, L"SYSTEM\\Media Player", NULL);

    do
    {
        m_bInitialized = TRUE;
    
        if(!cRegActive.IsKeyOpened())
        {
        	ERRMSG((L"[WAV:E] Opening Wave ActivePath registry is failed\r\n"));
        	m_bInitialized = FALSE;
        	break;
        }

        if(!cRegActive.GetRegValue(L"EnableCodecMaster", (LPBYTE)&m_bEnableCodecMaster, sizeof(m_bEnableCodecMaster)))
        {
        	ERRMSG((L"[WAV:E] Getting registry \"EnableCodecMaster\" is failed\r\n"));
        	m_bInitialized = FALSE;
        	break;
        }
//added by terry 20111001 for test 
	if(m_bEnableCodecMaster ==TRUE)
	{
		ERRMSG((L"[WAV:E] m_bEnableCodecMaster ==TRUE  CHANG TO false.  for WM8976      hehe! \r\n"));
		m_bEnableCodecMaster =FALSE;
	}
	else
	{
		ERRMSG((L"[WAV:E] m_bEnableCodecMaster ==FALSE %d\r\n"));
		
	}
	
        if(!cRegActive.GetRegValue(L"EnableLowPower", (LPBYTE)&m_bEnableLowPower, sizeof(m_bEnableLowPower)))
        {
        	ERRMSG((L"[WAV:E] Getting registry \"EnableLowPower\" is failed\r\n"));
        	m_bInitialized = FALSE;
        	break;
        }
        else
        {
            DWORD dwMP3BufferingTime;
            DWORD dwEnableLockstep;
            DWORD dwIdealHeartbeat;

            if(m_bEnableLowPower)
            {
                dwEnableLockstep    = 1;
                dwMP3BufferingTime  = 3000;
                dwIdealHeartbeat    = 1000;
            }
            else
            {
                dwEnableLockstep    = 0;
                dwMP3BufferingTime  = 1000;
                dwIdealHeartbeat    = 400;
            }

            if(!cRegSoftware.IsKeyOpened())
            {
            	ERRMSG((L"[WAV:E] Opening Software ActivePath registry is failed\r\n"));
            	m_bInitialized = FALSE;
            	break;
            }

            if(!cRegSoftware.RegSetValueEx(L"MP3BufferingTime", REG_DWORD, (LPBYTE)&dwMP3BufferingTime, sizeof(dwMP3BufferingTime)))
            {
                ERRMSG((L"[WAV:E] MP3BufferingTime setting is failed\r\n"));
            	m_bInitialized = FALSE;
            	break;
            }

            if(!cRegSystem.IsKeyOpened())
            {
            	ERRMSG((L"[WAV:E] Opening System ActivePath registry is failed\r\n"));
            	m_bInitialized = FALSE;
            	break;
            }

            if(!cRegSystem.RegSetValueEx(L"EnableLockstep", REG_DWORD, (LPBYTE)&dwEnableLockstep, sizeof(dwEnableLockstep)))
            {
            	ERRMSG((L"[WAV:E] EnableLockstep setting is failed\r\n"));
            	m_bInitialized = FALSE;
            	break;
            }

            if(!cRegSystem.RegSetValueEx(L"IdealHeartbeat", REG_DWORD, (LPBYTE)&dwIdealHeartbeat, sizeof(dwIdealHeartbeat)))
            {
            	ERRMSG((L"[WAV:E] IdealHeartbeat setting is failed\r\n"));
            	m_bInitialized = FALSE;
                break;
            }
        }

        m_DriverIndex       = Index;
        m_dwOutputGain      = 0xFFFF;
        m_dwInputGain       = 0xFFFF;
        m_bOutputMute       = FALSE;
        m_bInputMute        = FALSE;
        m_NumForcedSpeaker  = 0;

        if(!MapRegisters())
        {
            ERRMSG((L"[WAV:ERR] Initialize() : MapRegisters() Failed\n\r"));
            m_bInitialized = FALSE;
            break;
        }

        m_hPowerCon = CreateFile( L"PWC0:", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
        if(m_hPowerCon == INVALID_HANDLE_VALUE)
        {
            ERRMSG((L"[WAV:ERR] Initialize() : CreateFile() m_hPowerCon Open is failed\n\r"));
            m_bInitialized = FALSE;
            break;
        }
        else
        {
            DWORD dwBytes;
            DWORD dwIPIndex = PWR_IP_AUDIOSS;
            if(!DeviceIoControl(m_hPowerCon, IOCTL_PWRCON_SET_POWER_ON, &dwIPIndex, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
            {
                ERRMSG((L"[WAV:ERR] Initialize() : DeviceIoControl() IOCTL_PWRCON_SET_POWER_ON is failed\n\r"));
                m_bInitialized = FALSE;
                break;
            }
        }
            ERRMSG((L"[WAV] OpenI2C() ----------------------------------------\n\r"));
        m_hI2CCodec = OpenI2C(I2C_CODEC_GROUP_NAME);
      if(!m_hI2CCodec)
        {
            ERRMSG((L"[WAV:ERR] Initialize() : I2C Open is failed\n\r"));
			//
            m_bInitialized = FALSE;
            break;
        }

        if(!ClockPathEnable())
        {
            ERRMSG((L"[WAV:ERR] Initialize() : ClockPathEnable is failed\n\r"));
            m_bInitialized = FALSE;
            break;
        }
#if 1
        if(!InitI2C(m_hI2CCodec, I2C_CODEC_ID, I2C_CODEC_SPEED))
        {
            ERRMSG((L"[WAV:ERR] Initialize() : I2C Init is failed\n\r"));
            m_bInitialized = FALSE;
            break;
        }
#endif

        IIS_initialize_register_address((void *)m_pIISReg, (void *)m_pGPIOReg);
        IIS_initialize_interface(m_bEnableCodecMaster);

        if (!InitIISCodec())
        {
            ERRMSG((L"[WAV:ERR] Initialize() : InitIISCodec(() Failed\n\r"));
            m_bInitialized = FALSE;
            break;
        }

        m_hWaveEvent = CreateEvent(NULL, FALSE, FALSE, WAVE_EVENT_NAME);

        memset((void *)&msgOptions, 0x0, sizeof(msgOptions));
        msgOptions.dwSize = sizeof(MSGQUEUEOPTIONS);
        msgOptions.dwFlags = 0;
        msgOptions.dwMaxMessages = 1024;        // Max number of MSG queue entries
        msgOptions.cbMaxMessage = sizeof(MSG_DEV_STATE);
        msgOptions.bReadAccess = FALSE;
        m_hPwrMsgQue = CreateMsgQueue(OEMPM_MSGQ_NAME, &msgOptions);

        InitBasePointer(this);
        
        m_pOutLowPowerableMode = new OutLowPowerableMode(
            m_bEnableLowPower,
            &m_OutputDeviceContext, 
            &m_pCurrentInMode,
            BaseStartOutputDMA, 
            BaseStopOutputDMA);
        
        m_pOutNormalMode = new OutNormalMode(
            &m_OutputDeviceContext, 
            &m_pCurrentInMode,
            BaseStartOutputDMA, 
            BaseStopOutputDMA);

        m_pInNormalMode = new InNormalMode(
            &m_InputDeviceContext, 
            &m_pCurrentOutMode,
            BaseStartInputDMA, 
            BaseStopInputDMA);
        
        m_pOutLowPowerableMode->Init((LPWSTR)Index);
        m_pOutNormalMode->Init((LPWSTR)Index);
        m_pInNormalMode->Init((LPWSTR)Index);
        
       // m_pCurrentOutMode = m_pOutLowPowerableMode;
        m_pCurrentOutMode = m_pOutNormalMode;
       m_pCurrentInMode = m_pInNormalMode;

        m_PowerState = D0;
        m_bDVFSFixed = FALSE;
        m_bSRAMSet = FALSE;
        m_bULPARunning = FALSE;
        m_bInitialized = TRUE;
    } while(FALSE);

    DBGMSG(WAVE_INFO, (L"[WAV] Initializing is successful\r\n"));

    return m_bInitialized;
}


/*----------------------------------------------------------------------------
*Function: Deinitialize

*Parameters: None

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::Deinitialize()
{
    if (m_bInitialized)
    {
        StopOutputDMA();
        StopInputDMA();

        CodecMuteControl(DMA_CH_OUT|DMA_CH_IN, TRUE);

        UnMapRegisters();

        CloseHandle(m_hWaveEvent);

        CloseMsgQueue(m_hPwrMsgQue);

        m_pOutLowPowerableMode->Deinit();
        delete m_pOutLowPowerableMode;

        m_pOutNormalMode->Deinit();
        delete m_pOutNormalMode;

        m_pInNormalMode->Deinit();
        delete m_pInNormalMode;
    
        m_pCurrentOutMode = NULL;
        m_pCurrentInMode = NULL;
    }

    return TRUE;
}

BOOL        
HardwareContext::ClockPathEnable(VOID)
{
    //2 1. CLOCK_OUT SETTING
   
    m_pPMUMiscReg->SYS_CON.OTHERS &= ~(3<<8); // 0 : BY CLK_OUT SFR
                                              // 1 : RESERVED
                                              // 2 : XXTI(24Mhz)
                                              // 3 : XUSBXTI
    m_pSysConReg->CLK_OUTPUT.CLK_OUT = (CLK_OUT_XUSBXTI | BW_CLKOUT_DCLKEN );

    //2 2. AUDIO BUS && I2S CLOCK SETTING
    
    m_pASSClkReg->ASS_CLK_SRC = (0<<2)| // MUXI2S : 2(SCLK), 1(CDCLK), 0(MAIN)
                                (0<<1)| // MUXBUS: 1(CDCLK), 0(MAIN)
                                (1<<0); // CLKMUX: 1(EPLL), 0(XXTI)
                                
    m_pASSClkReg->ASS_CLK_DIV = (3<<4)| // I2S_A_RATIO
                                (0<<0); // AUD_BUS_RATIO

    m_pASSClkReg->ASS_CLK_GATE = (1<<6)| // I2S
                                 (1<<5)| // HCLK TO I2S
                                 (1<<4)| // HCLK TO UART
                                 (1<<3)| // HCLK TO HWA
                                 (1<<2)| // HCLK TO DMA
                                 (1<<1)| // HCLK TO BUF
                                 (1<<0); // HCLK TO RP

#if (S5PV210_EVT==0)
    DBGMSG(WAVE_INFO, (L"[WAV] EPLL_CLK : %d\r\n", GET_EPLLCLK(m_pSysConReg->PLL_CON.EPLL_CON)));
#else
    if (m_bEnableCodecMaster == FALSE)	
    {
	    DBGMSG(WAVE_DBG,(L"[WAV] EPLL S5PV210_EVT==1 \n\r"));
	    m_pSysConReg->PLL_CON.EPLL_CON0 = (1<<31) | (1<<27) | (67<<16) | (3<<8) | (3<<0);	// 67MHz  
	   m_pSysConReg->PLL_CON.EPLL_CON1 = 48366;	

	  // m_pSysConReg->PLL_CON.EPLL_CON0 = (1<<31) | (0<<27) | (45<<16) | (3<<8) | (3<<0);	// 67MHz  =>>>45158400
	  // m_pSysConReg->PLL_CON.EPLL_CON1 = 10355;	
    }
    DBGMSG(0, (L"[WAV] EPLL_CLK : %d\r\n", GET_EPLLCLK(m_pSysConReg->PLL_CON.EPLL_CON0)));
#endif
#ifdef WAV_I2S0
    //2 3. SCLK_AUDIO0 SETTING
    
    m_pSysConReg->CLK_SRC.CLK_SRC_MASK0 &= ~(BW_MUX_AUDIO0_MASK << BP_MUX_AUDIO0_MASK); 
    m_pSysConReg->CLK_SRC.CLK_SRC_MASK0 |= (1 << BP_MUX_AUDIO0_MASK); //MASK ENABLE
    // SCLK is not needed, this drivr will use CLK_OUT as master clock for master mode of AP
    m_pSysConReg->CLK_DIV.CLK_DIV6 &= ~(BW_DIV_AUDIO0_RATIO << BP_DIV_AUDIO0_RATIO);
    //masked by terry for esay210 m_pSysConReg->CLK_DIV.CLK_DIV6      |= (0 << BP_DIV_AUDIO1_RATIO); 	//modified by terry 20111003    0---->5
       m_pSysConReg->CLK_DIV.CLK_DIV6        |= (3 << BP_DIV_AUDIO0_RATIO); 	//modified by terry 20111003    0---->5
	
    m_pSysConReg->CLK_SRC.CLK_SRC6      &= ~(BW_MUX_AUDIO0_SEL << BP_MUX_AUDIO0_SEL);
    m_pSysConReg->CLK_SRC.CLK_SRC6      |= (CLK_SCLKEPLL << BP_MUX_AUDIO0_SEL); 
    //m_pSysConReg->CLK_GATE.CLK_GATE_IP3 |= (1 << BP_CLK_IP_I2S1) ; //I2S1 PASS
        m_pSysConReg->CLK_GATE.CLK_GATE_IP3 |= (1 << BP_CLK_IP_I2S0) ; //I2S0 PASS  modified by terry 20120313 for ease210 I2S0
#else //ifdef WAV_I2S1
    m_pSysConReg->CLK_SRC.CLK_SRC_MASK0 &= ~(BW_MUX_AUDIO1_MASK << BP_MUX_AUDIO1_MASK); 
    m_pSysConReg->CLK_SRC.CLK_SRC_MASK0 |= (1 << BP_MUX_AUDIO1_MASK); //MASK ENABLE
    // SCLK is not needed, this drivr will use CLK_OUT as master clock for master mode of AP
    m_pSysConReg->CLK_DIV.CLK_DIV6      &= ~(BW_DIV_AUDIO1_RATIO << BP_DIV_AUDIO1_RATIO);
    m_pSysConReg->CLK_DIV.CLK_DIV6      |= (0 << BP_DIV_AUDIO1_RATIO); 	//modified by terry 20111003    0---->5
    m_pSysConReg->CLK_SRC.CLK_SRC6      &= ~(BW_MUX_AUDIO1_SEL << BP_MUX_AUDIO1_SEL);
    m_pSysConReg->CLK_SRC.CLK_SRC6      |= (CLK_SCLKEPLL << BP_MUX_AUDIO1_SEL); 
    m_pSysConReg->CLK_GATE.CLK_GATE_IP3 |= (1 << BP_CLK_IP_I2S1) ; //I2S1 PASS




#endif

    return TRUE;
}


/*----------------------------------------------------------------------------
*Function: PowerUp

*Parameters: None

*Return Value: None

*Implementation Notes: 
-----------------------------------------------------------------------------*/
void
HardwareContext::PowerUp()
{
    DBGMSG(WAVE_PM,(L"[WAV] +++PowerUp()\n\r"));
#ifdef WAV_I2S0
    // Enable Clock for IIS CH0
    m_pSysConReg->CLK_GATE.CLK_GATE_IP3  |= (1 << BP_CLK_IP_I2S0) ;    
#else //WAV_I2S1
   m_pSysConReg->CLK_GATE.CLK_GATE_IP3  |= (1 << BP_CLK_IP_I2S1) ;    
#endif
    IIS_initialize_interface(m_bEnableCodecMaster);
    InitIISCodec();
    CodecMuteControl(DMA_CH_OUT|DMA_CH_IN, TRUE);

    DBGMSG(WAVE_PM,(L"[WAV] ---PowerUp()\n\r"));
}


/*----------------------------------------------------------------------------
*Function: PowerDown

*Parameters: None

*Return Value: None

*Implementation Notes: 
-----------------------------------------------------------------------------*/
void HardwareContext::PowerDown()
{
    DBGMSG(WAVE_PM,(L"[WAV] +++PowerDown()\n\r"));
    //SUSPEND POLICY
    //system can go into suspend mode when audio is sill playing
    //audio will be stopped automatically and be not restarted automatically after wake-up
    StopOutputDMA();
    StopInputDMA();
    CodecMuteControl(DMA_CH_OUT|DMA_CH_IN, TRUE);
    CodecPowerControl(FALSE, FALSE);
#ifdef WAV_I2S0
    m_pSysConReg->CLK_GATE.CLK_GATE_IP3  &= ~(1 << BP_CLK_IP_I2S0) ;        
#else
	m_pSysConReg->CLK_GATE.CLK_GATE_IP3  &= ~(1 << BP_CLK_IP_I2S1) ;        
#endif
    DBGMSG(WAVE_PM,(L"[WAV] ---PowerDown()\n\r"));
}


/*----------------------------------------------------------------------------
*Function: Open

*Parameters: None

*Return Value: mmErr

*Implementation Notes: 
-----------------------------------------------------------------------------*/

DWORD
HardwareContext::Open(void)
{
    DWORD mmErr = MMSYSERR_NOERROR;
    // Don't allow play when not on, if there is a power constraint upon us.

    DBGMSG(WAVE_PM,(L"[WAV] +++Open()\n\r"));

    // Don't allow play when not on, if there is a power constraint upon us.
    /*
    if ( D0 != m_PowerState )
    {
        // Tell the Power Manager we need to power up.
        // If there is a power constraint then fail.
        DevicePowerNotify(WAVE_POWER_NAME, D0, POWER_NAME);
    }*/

    DBGMSG(WAVE_PM,(L"[WAV] +++Open()\n\r"));

    return mmErr;
}


/*----------------------------------------------------------------------------
*Function: Close

*Parameters: None

*Return Value: mmErr

*Implementation Notes: 
-----------------------------------------------------------------------------*/
DWORD
HardwareContext::Close(void)
{
    DWORD mmErr = MMSYSERR_NOERROR;

    DBGMSG(WAVE_PM,(L"[WAV] +++Close()\n\r"));

    // we are done so inform Power Manager to power us down, 030711
    //DevicePowerNotify(WAVE_POWER_NAME, (_CEDEVICE_POWER_STATE)D4, POWER_NAME);

    DBGMSG(WAVE_PM,(L"[WAV] +++Close()\n\r"));

    return mmErr;
}

CEDEVICE_POWER_STATE
HardwareContext::SetPowerState(
    CEDEVICE_POWER_STATE requestState
    )
{
    BOOL bRtn = TRUE;

    __try
    {
    	switch(requestState)
    	{
            case D0:
            case D1:
            case D3:
            	requestState = D0;
            	break;
            case D2:
                requestState = D2;
            	break;
            case D4:
            	requestState = D4;
            	break;
            default:
            	requestState = m_PowerState;
            	break;
    	}

        if(m_PowerState != requestState)
        {
            BOOL bSend = FALSE;
            MSG_DEV_STATE Msg;

            Msg.bLPModeSupport = TRUE;
            Msg.dwDeviceID = 116;
            Msg.dwMsg = DTOP_POWER_SET;
            Msg.dwLParam = requestState;
            
        	if(requestState == D0)
        	{
/*#ifdef BSP_USEDVFS
                DWORD Profile;
                DWORD dwBytes;	
                
                if(!m_bDVFSFixed)
                {
                    Profile = HIGH_PERF;
                    if(!DeviceIoControl(m_hPowerCon, IOCTL_DVFS_SET_PROFILE, &Profile, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
                    {
                        ERRMSG((L"[WAV] ERROR : It's failed to fix DVFS into HIGH_PERF\r\n"));
                    }
                    else
                        m_bDVFSFixed = TRUE;
                }  
#endif*/
                if(m_PowerState == D4) PowerUp();
                m_pCurrentOutMode->SetPowerState(requestState);
                m_pCurrentInMode->SetPowerState(requestState);
                DBGMSG(WAVE_INFO,(L"[WAV] SEND D0 TO POWERCON\r\n"));
                bSend = TRUE;
            }
            else if(requestState == D2)
            {
/*#ifdef BSP_USEDVFS
                DWORD Profile;
                DWORD dwBytes;	

                if(m_bDVFSFixed)
                {
                    Profile = HIGH_PERF;
                    if(!DeviceIoControl(m_hPowerCon, IOCTL_DVFS_CLEAR_PROFILE, &Profile, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
                    {
                        ERRMSG((L"[WAV] ERROR : It's failed to clear fixed DVFS value\r\n"));
                    }  
                    else
                        m_bDVFSFixed = FALSE;
                }                          
#endif*/            
                if(m_PowerState == D4) PowerUp();
                m_pCurrentOutMode->SetPowerState(requestState);
                m_pCurrentInMode->SetPowerState(requestState);
                DBGMSG(WAVE_INFO,(L"[WAV] SEND D2 TO POWERCON\r\n"));
                bSend = TRUE;
            }
            else if(requestState == D4)
            {
                m_pCurrentOutMode->SetPowerState(requestState);
                m_pCurrentInMode->SetPowerState(requestState);
                PowerDown();
                DBGMSG(WAVE_INFO,(L"[WAV] SEND D4 TO POWERCON\r\n"));
                bSend = TRUE;
            }

            m_PowerState = requestState;

            if(bSend)
            {
                if(!WriteMsgQueue(m_hPwrMsgQue, &Msg, sizeof(MSG_DEV_STATE), INFINITE, MSGQUEUE_MSGALERT))
                {
                    ERRMSG((L"WriteMsgQueue is failed\r\n"));
                }
            }
        } 
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        ERRMSG((L"[WAV] SetPowerState EXCEPTION\r\n"));
    }

    return m_PowerState;
}

CEDEVICE_POWER_STATE 
HardwareContext::GetPowerState(VOID)
{
    return m_PowerState;
}

/*----------------------------------------------------------------------------
*Function: IOControl

*Parameters: 
			DWORD  dwOpenData,
			DWORD  dwCode,
			PBYTE  pBufIn,
			DWORD  dwLenIn,
			PBYTE  pBufOut,
			DWORD  dwLenOut,
			PDWORD pdwActualOut


*Return Value: True/False

*Implementation Notes: This function will allocate memory from virtual address space and initialize the register variable.
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::IOControl(
            DWORD  dwOpenData,
            DWORD  dwCode,
            PBYTE  pBufIn,
            DWORD  dwLenIn,
            PBYTE  pBufOut,
            DWORD  dwLenOut,
            PDWORD pdwActualOut)
{
    BOOL  bResult = TRUE;

    UNREFERENCED_PARAMETER(dwOpenData);

    switch (dwCode)
    {
        case IOCTL_WAVE_ULPA_SET:
            {
                Lock();
                DBGMSG(0, (L"[WAV] +++IOCTL_WAVE_ULPA_SET\r\n"));
                
                IIS_initialize_interface(TRUE);
                m_bULPARunning = TRUE;
                m_pASSCommBoxReg->MISC |= (1<<3);
                m_pOutNormalMode->ULPASet();
                //IIS_print_all();
                CodecPowerControl(TRUE, m_pCurrentInMode->GetRunningStatus());  
                CodecMuteControl(DMA_CH_OUT, FALSE);

                DBGMSG(TRUE, (L"[WAV] EPLL_CLK : %d\r\n", GET_EPLLCLK(m_pSysConReg->PLL_CON.EPLL_CON0)));
                DBGMSG(TRUE, (L"[WAV] ASS_CLK_SRC : 0x%X\r\n", m_pASSClkReg->ASS_CLK_SRC));
                DBGMSG(TRUE, (L"[WAV] ASS_CLK_DIV : 0x%X\r\n", m_pASSClkReg->ASS_CLK_DIV));

                DBGMSG(WAVE_INFO, (L"[WAV] ---IOCTL_WAVE_ULPA_SET\r\n"));
                Unlock();
            }
            break;
            
        case IOCTL_WAVE_ULPA_CLR:
            {
                Lock();
                DBGMSG(0, (L"[WAV] +++IOCTL_WAVE_ULPA_CLR\r\n"));

                if(m_bULPARunning)
                {
                    BOOL bTxOn = m_pCurrentOutMode->GetRunningStatus();
                    BOOL bRxOn = m_pCurrentInMode->GetRunningStatus();

                    m_bULPARunning = FALSE;
                    m_pASSCommBoxReg->MISC &= ~(1<<3);
                    m_pOutNormalMode->ULPAClr();
                    CodecMuteControl(DMA_CH_OUT, !bTxOn);
                    CodecPowerControl(bTxOn, bRxOn); 
                    /*if(!bTxOn && !bRxOn)
                    { 
                        IIS_set_active_off();
                    }*/
                    IIS_initialize_interface(m_bEnableCodecMaster);
                }

                DBGMSG(WAVE_INFO, (L"[WAV] ---IOCTL_WAVE_ULPA_CLR\r\n"));
                Unlock();
            }
            break;

        case IOCTL_WAVE_SRAM_SET:
            {
                DWORD dwResult;
                
                Lock();
                DBGMSG(0, (L"[WAV] +++IOCTL_WAVE_SRAM_SET\r\n"));

                if((dwLenOut >= sizeof(BOOL)) && (pBufOut != NULL) && !m_bSRAMSet)
                {
                    if(m_pCurrentOutMode->GetRunningStatus())
                    {
                        m_pCurrentOutMode->m_bModeChange = TRUE;
                        m_pCurrentOutMode->m_pModeChangeParam1 = m_pOutNormalMode;
                        dwResult = WaitForSingleObject(m_hWaveEvent, WAVE_EVENT_TIME);
                        if(dwResult == WAIT_OBJECT_0)
                        {
                            //IIS_print_all();
                            m_bSRAMSet = TRUE;
                            m_pCurrentOutMode = m_pOutNormalMode;
                            DevicePowerNotify(WAVE_POWER_NAME, D0, POWER_NAME);
                        }
                        else
                        {
                            ERRMSG((L"[WAV] IOControl(IOCTL_WAVE_ULPA_SET) : WaitForSingleObject is failed\r\n"));
                            bResult = FALSE;
                        }
                    }
                    else
                    {
                        m_bSRAMSet = TRUE;
                        m_pCurrentOutMode = m_pOutNormalMode;
                        m_pCurrentOutMode->SetPowerState(D2);
                    }
                    
                    *pBufOut = TRUE;
                }

                else
                {
                    ERRMSG((L"[WAV] IOControl(IOCTL_WAVE_ULPA_SET) : A parameter is invalid\r\n"));
                    bResult = FALSE;
                }

                DBGMSG(WAVE_INFO, (L"[WAV] ---IOCTL_WAVE_SRAM_SET\r\n"));
                Unlock();
            }
            break;
            
        case IOCTL_WAVE_SRAM_CLR:
            {
                DWORD dwResult;

                Lock();
                DBGMSG(0, (L"[WAV] +++IOCTL_WAVE_SRAM_CLR\r\n"));

                if((dwLenOut >= sizeof(BOOL)) && (pBufOut != NULL) && m_bSRAMSet)
                {
                    if(m_pCurrentOutMode->GetRunningStatus())
                    {
                        m_pCurrentOutMode->m_bModeChange = TRUE;
                        m_pCurrentOutMode->m_pModeChangeParam1 = m_pOutLowPowerableMode;
                        dwResult = WaitForSingleObject(m_hWaveEvent, WAVE_EVENT_TIME);
                        if(dwResult == WAIT_OBJECT_0)
                        {
                            m_bSRAMSet = FALSE;
                            m_pCurrentOutMode = m_pOutLowPowerableMode;
                            DevicePowerNotify(WAVE_POWER_NAME, D0, POWER_NAME);
                        }
                        else
                        {
                            ERRMSG((L"[WAV] IOControl(IOCTL_WAVE_ULPA_CLR) : WaitForSingleObject is failed\r\n"));
                            bResult = FALSE;
                        }
                    }
                    else
                    {
                        m_bSRAMSet = FALSE;
                        m_pCurrentOutMode = m_pOutLowPowerableMode;
                        m_pCurrentOutMode->SetPowerState(D2);
                    }
                    
                    *pBufOut = TRUE;
                }

                else
                {
                    ERRMSG((L"[WAV] IOControl(IOCTL_WAVE_ULPA_SET) : A parameter is invalid\r\n"));
                    bResult = FALSE;
                }

                DBGMSG(0, (L"[WAV] ---IOCTL_WAVE_SRAM_CLR\r\n"));
                Unlock();
            }
            break;

        case IOCTL_WAVE_MEDIUM_INFO_GET:
            DBGMSG(0, (L"[WAV] +++IOCTL_WAVE_MEDIUM_INFO_GET\r\n"));

            if(dwLenOut >= sizeof(MEDIUM_INFO))
            {
                PMEDIUM_INFO pMediumInfo    = (PMEDIUM_INFO)pBufOut;
                pMediumInfo->MediumClass    = MEDIUM_IIS;
                pMediumInfo->DataFormat     = DFORMAT_IIS;
                pMediumInfo->Polarity       = LEFT_LOW;
                pMediumInfo->BitClk         = BCLK_32;
                pMediumInfo->BitLength      = BLENGTH_16;
                pMediumInfo->dwLRClk        = 44100;
                *pdwActualOut               = sizeof(MEDIUM_INFO);
            }
            
            DBGMSG(0, (L"[WAV] ---IOCTL_WAVE_MEDIUM_INFO_GET\r\n"));
            break;
            
        case IOCTL_POWER_CAPABILITIES:
            DBGMSG(WAVE_PM, (L"[WAV] +++IOCTL_POWER_CAPABILITIES\r\n"));

            if (pBufOut && pdwActualOut && (dwLenOut >= sizeof(POWER_CAPABILITIES)))
            {
                DevicePowerNotify(WAVE_POWER_NAME, D2, POWER_NAME);

                __try
                {
                    PPOWER_CAPABILITIES PowerCaps = (PPOWER_CAPABILITIES)pBufOut;
                    memset(PowerCaps, 0, sizeof(*PowerCaps));
                    PowerCaps->DeviceDx = (UCHAR)GetSupportablePowerState();
                    PowerCaps->Flags = POWER_CAP_PREFIX_MICRO | POWER_CAP_UNIT_AMPS;
                    PowerCaps->Power[D0] = 302; //UNIT : Milli Watt
                    PowerCaps->Power[D2] = 64;
                    PowerCaps->Power[D4] = 0;
                    *pdwActualOut = sizeof(*PowerCaps);
                }
                __except(EXCEPTION_EXECUTE_HANDLER)
                {
                	ERRMSG((L"[WAV] IOCTL_POWER_CAPABILITIES EXCEPTION\r\n"));
                    bResult = FALSE;
                }
            }
            
            DBGMSG(WAVE_PM, (L"[WAV] ---IOCTL_POWER_CAPABILITIES\r\n"));
            break;

        case IOCTL_POWER_QUERY:
            DBGMSG(WAVE_PM, (L"[WAV] +++IOCTL_POWER_QUERY\r\n"));
            
            if (pBufOut && dwLenOut >= sizeof(CEDEVICE_POWER_STATE)) 
            {
                __try 
                {
                    CEDEVICE_POWER_STATE PowerState = *(PCEDEVICE_POWER_STATE)pBufOut;
                    if(!VALID_DX(PowerState)) bResult = FALSE;
                }
                __except(EXCEPTION_EXECUTE_HANDLER)
                {
                    ERRMSG((L"[WAV] IOCTL_POWER_QUERY EXCEPTION\r\n"));
                    bResult = FALSE;
                }
            }

            DBGMSG(WAVE_PM, (L"[WAV] ---IOCTL_POWER_QUERY\r\n"));
            break;     

        case IOCTL_POWER_SET:
            DBGMSG(WAVE_PM, (L"[WAV] +++IOCTL_POWER_SET\r\n"));
                
            if (pBufOut && dwLenOut >= sizeof(CEDEVICE_POWER_STATE))
            {
                __try
                {
                    CEDEVICE_POWER_STATE PowerState = *(PCEDEVICE_POWER_STATE)pBufOut;
                    DBGMSG(WAVE_PM, (L"[WAV] IOCTL_POWER_SET : D%d \r\n", PowerState));

                    if (VALID_DX(PowerState))
                    {
                        *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
                        *(PCEDEVICE_POWER_STATE) pBufOut = SetPowerState(PowerState);
                    }
                }
                __except(EXCEPTION_EXECUTE_HANDLER)
                {
                	ERRMSG((L"[WAV] IOCTL_POWER_SET EXCEPTION\r\n"));
                	bResult = FALSE;
                }
            }

            DBGMSG(WAVE_PM, (L"[WAV] ---IOCTL_POWER_SET\r\n"));
            break;

        case IOCTL_POWER_GET:
            DBGMSG(WAVE_PM, (L"[WAV] +++IOCTL_POWER_GET\r\n"));

    		if (pBufOut != NULL && dwLenOut >= sizeof(CEDEVICE_POWER_STATE))
    		{
    			__try
    			{
    				*(PCEDEVICE_POWER_STATE)pBufOut = GetPowerState();
    				DBGMSG(WAVE_PM, (L"[WAV] IOCTL_POWER_GET : D%d \r\n", *pBufOut));

    			}
    			__except(EXCEPTION_EXECUTE_HANDLER)
    			{
    				ERRMSG((L"[WAV] IOCTL_POWER_GET EXCEPTION\r\n"));
    		        			bResult = FALSE;
    			}
    		}
                
            DBGMSG(WAVE_PM, (L"[WAV] ---IOCTL_POWER_GET\r\n"));
            break;

        default:
            ERRMSG((L"[WAV] UNKNOWN IOCTL(%d)\r\n", dwCode));
    		bResult = FALSE;
    		break;
    }

    return bResult;
}

/*----------------------------------------------------------------------------
*Function: StartOutputDMA

*Parameters: None

*Return Value: None

*Implementation Notes: 
-----------------------------------------------------------------------------*/
VOID
HardwareContext::StartOutputDMA(VOID)
{    
    Lock();
    
    if(!m_pCurrentOutMode->GetRunningStatus() && (GetPowerState() != D4))
    {
        DBGMSG(0,(L"[WAV] +++HW_OutStart\r\n"));

        //DBGMSG(TRUE, (L"[WAV] EPLL_CLK : %d\r\n", GET_EPLLCLK(m_pSysConReg->PLL_CON.EPLL_CON0)));
        //DBGMSG(TRUE, (L"[WAV] ASS_CLK_SRC : 0x%X\r\n", m_pASSClkReg->ASS_CLK_SRC));
        //DBGMSG(TRUE, (L"[WAV] ASS_CLK_DIV : 0x%X\r\n", m_pASSClkReg->ASS_CLK_DIV));
        //FORCE PWR NOTIFY
        //It's good in case following
        //ULP : DON'T CARE
        //OUT : STOP
        //IN  : DON'T CARE
        DevicePowerNotify(WAVE_POWER_NAME, D0, POWER_NAME);
        
        m_pCurrentOutMode->StartTransfer();
        CodecPowerControl(TRUE, m_pCurrentInMode->GetRunningStatus());  
        CodecMuteControl(DMA_CH_OUT, FALSE);
        
        DBGMSG(0,(L"[WAV] ---HW_OutStart\r\n"));
    }

    Unlock();
}

/*----------------------------------------------------------------------------
*Function: StopOutputDMA

*Parameters: None

*Return Value: None

*Implementation Notes: 
-----------------------------------------------------------------------------*/
VOID
HardwareContext::StopOutputDMA(VOID)
{
    Lock();
    
    if(m_pCurrentOutMode->GetRunningStatus())
    {
        DBGMSG(0,(L"[WAV] +++HW_OutStop\r\n"));

        if(m_bULPARunning)
        {
            m_pCurrentOutMode->StopTransfer(FALSE);
        }
        else
        {
            m_pCurrentOutMode->StopTransfer(!m_pCurrentInMode->GetRunningStatus());
      
            CodecMuteControl(DMA_CH_OUT, TRUE);
            CodecPowerControl(FALSE, m_pCurrentInMode->GetRunningStatus());  
        }
        
        //FORCE PWR NOTIFY
        //It's good in case following
        //ULP : DON'T CARE
        //OUT : STOP
        //IN  : STOP
        if(!m_pCurrentInMode->GetRunningStatus())
        DevicePowerNotify(WAVE_POWER_NAME, D2, POWER_NAME); 
        
        DBGMSG(0,(L"[WAV] ---HW_OutStop\r\n"));
    }

    Unlock();
}

/*----------------------------------------------------------------------------
*Function: StartInputDMA

*Parameters: None

*Return Value: None

*Implementation Notes: 
-----------------------------------------------------------------------------*/
VOID
HardwareContext::StartInputDMA(VOID)
{
    Lock();
    
    if(!m_pCurrentInMode->GetRunningStatus() && (GetPowerState() != D4))
    {
        DBGMSG(0,(L"[WAV] +++HW_InStart\r\n"));

        //FORCE PWR NOTIFY
        //It's good in case following
        //ULP : DON'T CARE
        //OUT : DON'T CARE
        //IN  : STOP
        DevicePowerNotify(WAVE_POWER_NAME, D0, POWER_NAME);
        
        m_pCurrentInMode->StartTransfer();
        CodecPowerControl(m_pCurrentOutMode->GetRunningStatus(), TRUE); 
        CodecMuteControl(DMA_CH_IN, FALSE);    // Unmute Input Channel        
        
        DBGMSG(0,(L"[WAV] ---HW_InStart\r\n"));
    }

    Unlock();
}

/*----------------------------------------------------------------------------
*Function: StopInputDMA

*Parameters: None

*Return Value: None

*Implementation Notes: 
-----------------------------------------------------------------------------*/
VOID
HardwareContext::StopInputDMA(VOID)
{
    Lock();

    if(m_pCurrentInMode->GetRunningStatus())
    {
        DBGMSG(0,(L"[WAV] +++HW_InStop\r\n"));

        if(m_bULPARunning)
        {
            m_pCurrentInMode->StopTransfer(FALSE);
            CodecMuteControl(DMA_CH_IN, TRUE);
            CodecPowerControl(TRUE, FALSE); 
        }
        else
        {
            m_pCurrentInMode->StopTransfer(!m_pCurrentOutMode->GetRunningStatus());
            CodecMuteControl(DMA_CH_IN, TRUE);
            CodecPowerControl(m_pCurrentOutMode->GetRunningStatus(), FALSE); 
        }
        
        //FORCE PWR NOTIFY
        //It's good in case following
        //ULP : DON'T CARE
        //OUT : STOP
        //IN  : STOP
        if(!m_pCurrentOutMode->GetRunningStatus())
        DevicePowerNotify(WAVE_POWER_NAME, D2, POWER_NAME); 
        
        DBGMSG(0,(L"[WAV] ---HW_InStop\r\n"));
    }

    Unlock();
}

/*----------------------------------------------------------------------------
*Function: GetOutputGain

*Parameters: None

*Return Value: m_dwOutputGain

*Implementation Notes: 
-----------------------------------------------------------------------------*/
DWORD
HardwareContext::GetOutputGain (void)
{
    return m_dwOutputGain;
}

/*----------------------------------------------------------------------------
*Function: SetOutputGain

*Parameters: dwGain

*Return Value: MMRESULT

*Implementation Notes: 
-----------------------------------------------------------------------------*/
MMRESULT
HardwareContext::SetOutputGain (DWORD dwGain)
{
    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] ++SetOutputGain(0x%08x)\r\n"), dwGain));

    m_dwOutputGain = dwGain & 0xffff;    // save off so we can return this from GetGain - but only MONO

    // convert 16-bit gain to 5-bit attenuation
    UCHAR ucGain;
    if (m_dwOutputGain == 0)
    {
        ucGain = 0x3F; // mute: set maximum attenuation
    }
    else
    {
        ucGain = (UCHAR) ((0xffff - m_dwOutputGain) >> 11);    // codec supports 64dB attenuation, we'll only use 32
    }
    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] --SetOutputGain(0x%08x)\r\n"), dwGain));

    return MMSYSERR_NOERROR;
}

/*----------------------------------------------------------------------------
*Function: GetInputGain

*Parameters: None

*Return Value: m_dwInputGain

*Implementation Notes: 
-----------------------------------------------------------------------------*/
DWORD
HardwareContext::GetInputGain (void)
{
    return m_dwInputGain;
}

/*----------------------------------------------------------------------------
*Function: SetInputGain

*Parameters: dwGain

*Return Value: MMRESULT

*Implementation Notes:
-----------------------------------------------------------------------------*/
MMRESULT
HardwareContext::SetInputGain (DWORD dwGain)
{
    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] ++SetInputGain(0x%08x)\r\n"), dwGain));

    m_dwInputGain = dwGain;

    if (!m_bInputMute)
    {
        m_InputDeviceContext.SetGain(dwGain);
    }

    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] --SetInputGain(0x%08x)\r\n"), dwGain));
    return MMSYSERR_NOERROR;
}

/*----------------------------------------------------------------------------
*Function: GetOutputMute

*Parameters: None

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::GetOutputMute (void)
{
    return m_bOutputMute;
}

/*----------------------------------------------------------------------------
*Function: SetOutputMute

*Parameters: bMute

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
MMRESULT
HardwareContext::SetOutputMute (BOOL bMute)
{
    DEBUGMSG(ZONE_FUNCTION,(TEXT("++SetOutputMute(%d)\n\r"), bMute));

    m_bOutputMute = bMute;

    if (bMute)
    {
     //   WriteCodecRegister(WM8580_DAC_CONTROL5, 0x010);
    }
    else
    {
      //  WriteCodecRegister(WM8580_DAC_CONTROL5, 0x000);
    }

    DEBUGMSG(ZONE_FUNCTION,(TEXT("--SetOutputMute()\n\r")));

    return MMSYSERR_NOERROR;
}

/*----------------------------------------------------------------------------
*Function: GetInputMute

*Parameters: None

*Return Value: True/False

*Implementation Notes:
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::GetInputMute (void)
{
    return m_bInputMute;
}

/*----------------------------------------------------------------------------
*Function: SetInputMute

*Parameters: bMute

*Return Value: MMRESULT

*Implementation Notes:
-----------------------------------------------------------------------------*/
MMRESULT
HardwareContext::SetInputMute (BOOL bMute)
{
    m_bInputMute = bMute;
    return m_InputDeviceContext.SetGain(bMute ? 0: m_dwInputGain);
}

/*----------------------------------------------------------------------------
*Function: ForceSpeaker

*Parameters: bForceSpeaker

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
DWORD
HardwareContext::ForceSpeaker(BOOL bForceSpeaker)
{
    // If m_NumForcedSpeaker is non-zero, audio should be routed to an
    // external speaker (if hw permits).
    if (bForceSpeaker)
    {
        m_NumForcedSpeaker++;
        if (m_NumForcedSpeaker == 1)
        {
            SetSpeakerEnable(TRUE);
        }
    }
    else
    {
        m_NumForcedSpeaker--;
        if (m_NumForcedSpeaker ==0)
        {
            SetSpeakerEnable(FALSE);
        }
    }

    return MMSYSERR_NOERROR;
}

/*----------------------------------------------------------------------------
*Function: MapRegisters

*Parameters: None

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::MapRegisters()
{
    BOOL bMap = TRUE;
    PHYSICAL_ADDRESS    ioPhysicalBase = {0,0};

    DBGMSG(0,(TEXT("[WAV] +++MapRegisters()\n\r")));

    do
    {
        ioPhysicalBase.LowPart = BASE_REG_PA_GPIO;
        m_pGPIOReg = (PGPIO_REG)MmMapIoSpace(ioPhysicalBase, sizeof(GPIO_REG), FALSE);
        if (m_pGPIOReg == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pGPIOReg MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }
#ifdef WAV_I2S0
        ioPhysicalBase.LowPart = BASE_REG_PA_I2S0; // modified by terry for esay210 i2s0 2012.03.13 
#else //ifdef WAV_I2S1
        ioPhysicalBase.LowPart = BASE_REG_PA_I2S1; 
#endif
        m_pIISReg = (IIS_REG *)MmMapIoSpace(ioPhysicalBase, sizeof(IIS_REG), FALSE);
        if (m_pIISReg == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pIISReg MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }

        ioPhysicalBase.LowPart = BASE_REG_PA_CMU_CLK;
        m_pSysConReg = (PCMU_CLK_REG)MmMapIoSpace(ioPhysicalBase, sizeof(CMU_CLK_REG), FALSE);
        if (m_pSysConReg == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pSysConReg MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }

        ioPhysicalBase.LowPart = BASE_REG_PA_PMU_MISC;
        m_pPMUMiscReg = (PPMU_MISC_REG)MmMapIoSpace(ioPhysicalBase, sizeof(PMU_MISC_REG), FALSE);
        if (m_pPMUMiscReg == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pPMUMiscReg MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }

        ioPhysicalBase.LowPart = BASE_REG_PA_ASS_CLKCON;
        m_pASSClkReg = (PASS_CLK_REG)MmMapIoSpace(ioPhysicalBase, sizeof(ASS_CLK_REG), FALSE);
        if (m_pASSClkReg == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pASSClkReg MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }

        ioPhysicalBase.LowPart = BASE_REG_PA_ASS_COMMBOX3;
        m_pASSCommBoxReg = (PASS_CBOX3_REG)MmMapIoSpace(ioPhysicalBase, sizeof(ASS_CBOX3_REG), FALSE);
        if (m_pASSCommBoxReg == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pASSCommBoxReg MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }

        ioPhysicalBase.LowPart = IMAGE_SHARE_ARGS_PA_START;
        m_pBSPArgs = (BSP_ARGS *)MmMapIoSpace(ioPhysicalBase, sizeof(BSP_ARGS), FALSE);
        if (m_pBSPArgs == NULL)
        {
            ERRMSG((L"[WAV:ERR] MapRegisters() : m_pBSPArgs MmMapIoSpace() Failed\n\r"));
            bMap = FALSE;
            break;
        }
    } while(FALSE);
  

    if (!bMap)
    {
        UnMapRegisters();
    }

    DBGMSG(0,(TEXT("[WAV] ---MapRegisters()\n\r")));

    return bMap;
}

/*----------------------------------------------------------------------------
*Function: UnMapRegisters

*Parameters: None

*Return Value: True/False

*Implementation Notes: 
-----------------------------------------------------------------------------*/
BOOL
HardwareContext::UnMapRegisters()
{
    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] UnMapRegisters()\n\r")));

    if (m_pGPIOReg != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pGPIOReg, sizeof(GPIO_REG));
        m_pGPIOReg = NULL;
    }

    if (m_pIISReg != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pIISReg, sizeof(IIS_REG));
        m_pIISReg = NULL;
    }

    if (m_pASSClkReg != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pASSClkReg, sizeof(ASS_CLK_REG));
        m_pASSClkReg = NULL;
    }

    if (m_pASSCommBoxReg != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pASSCommBoxReg, sizeof(ASS_CBOX2_REG));
        m_pASSCommBoxReg = NULL;
    }

    if (m_pSysConReg != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pSysConReg, sizeof(CMU_CLK_REG));
        m_pSysConReg = NULL;
    }

    if (m_pPMUMiscReg != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pPMUMiscReg, sizeof(PPMU_MISC_REG));
        m_pPMUMiscReg = NULL;
    }

    if (m_pBSPArgs != NULL)
    {
        MmUnmapIoSpace((PVOID)m_pBSPArgs, sizeof(BSP_ARGS));
        m_pBSPArgs = NULL;
    }

    return TRUE;
}

BOOL
HardwareContext::InitIISCodec()
{
    DWORD   dwErr = TRUE;
    DWORD   bytes = 0;

    DBGMSG(0,(L"[WAV] ++InitIISCodec()\n\r"));

    I2S_Init8580Driver();

    DBGMSG(0,(L"[WAV] --InitIISCodec()\n\r"));

    return dwErr;
}

void
HardwareContext::WriteCodecRegister(WORD wReg, WORD wVal)
{
    BYTE btWriteBuffer[2];
   // btWriteBuffer[0] = (BYTE)(wReg<<1)|(BYTE)((wVal>>8)&1);

// modified by terry 20111006
   btWriteBuffer[0] = (BYTE)(wReg);
  
    btWriteBuffer[1] = (BYTE)(wVal&0xFF);
   
    WriteI2C_CODEC(m_hI2CCodec, btWriteBuffer, 2);

    DBGMSG(0, (L"[WAV] WRITE ADDR : 0x%04X, DATA : 0x%04X\r\n", wReg, wVal));
}




WORD
HardwareContext::ReadCodecRegister(WORD wReg)
{
    WORD wVal;

    //-----------------------------------------------------------------------------------------------------
    // !!!!! Caution !!!!!
    // WM8580 support write operation only.
    // I write sfr data value onto internal table(m_WM8580_SFR_Table), on every write operation for backup.
    // And To support read operation, I just return some value from internal table(m_WM8580_SFR_Table).
    //-----------------------------------------------------------------------------------------------------

//BOOL  ReadI2C_CODEC(HANDLE hI2C, PBYTE pWData, DWORD dwWDatas, BOOL bStop, PBYTE pRData, DWORD dwRDatas)
	if(ReadI2C_CODEC(m_hI2CCodec,NULL,NULL, NULL, (PBYTE)(&wReg), wVal))
	{
		DBGMSG(0, (L"[WAV] READ ADDR : 0x%04X, DATA : 0x%04X\r\n", wReg, wVal));
	}
	else
	{
		DBGMSG(0, (L"[WAV] ReadI2C_CODEC...................\r\n"));

		return false;
	}

	
	//wVal = m_WM8580_SFR_Table[wReg];

//    DBGMSG(0, (L"[WAV] READ ADDR : 0x%04X, DATA : 0x%04X\r\n", wReg, wVal));

    return wVal;
}



BOOL
HardwareContext::CodecPowerControl(BOOL bOnTxPwr, BOOL bOnRxPwr)
{
            DBGMSG(0,(L"CodecPowerControl() -\r\n"));
    return(TRUE);	//added by terry 20111002
	
    if(bOnTxPwr && bOnRxPwr)
    {
        DBGMSG(WAVE_INFO,(L"Codec_channel() - In & Out\r\n"));
        WriteCodecRegister(WM8580_PWRDN1, WM8580_ALL_POWER_ON);                    // ADC, DAC power up
    }
    else if(bOnRxPwr)
    {
        DBGMSG(WAVE_INFO,(L"Codec_channel() - In\r\n"));
        WriteCodecRegister(WM8580_PWRDN1, WM8580_PWRDN1_ADCPD_ENABLE|WM8580_PWRDN1_DACPD_ALL_DISABLE);        // ADC power up, DAC power down
    }
    else if(bOnTxPwr)
    {
        DBGMSG(WAVE_INFO,(L"Codec_channel() - Out\r\n"));
        WriteCodecRegister(WM8580_PWRDN1, WM8580_PWRDN1_DACPD_ALL_ENABLE|WM8580_PWRDN1_ADCPD_DISABLE);   // DAC power up, ADC power down
    }
    else
    {
        DBGMSG(WAVE_INFO,(L"Codec_channel() - none\r\n"));
        WriteCodecRegister(WM8580_PWRDN1, WM8580_PWRDN1_ADCPD_DISABLE|WM8580_PWRDN1_DACPD_ALL_DISABLE ); // ADC/DAC power down
    }
    
    return(TRUE);
}


BOOL
HardwareContext::CodecMuteControl(DWORD channel, BOOL bMute)
{
	return (TRUE); //added by terry 20111002
    if(channel & DMA_CH_OUT)
    {
        if(bMute)
        {
            WriteCodecRegister(WM8580_DAC_CONTROL5, 0x010);
        }
        else
        {
            WriteCodecRegister(WM8580_DAC_CONTROL5, 0x000);
        }
    }
    if(channel & DMA_CH_IN)
    {
        if(bMute)
        {
            WriteCodecRegister(WM8580_ADC_CONTROL1, 0x044);
        }
        else
        {
            WriteCodecRegister(WM8580_ADC_CONTROL1, 0x040);
        }
    }
    return(TRUE);
}



void HardwareContext::SetSpeakerEnable(BOOL bEnable)
{
    // Code to turn speaker on/off here
    return;
}

void 
HardwareContext::WM8580_CodecInitPCMOutwithVol(
							CODECport eCodecPort,
							I2SOPMode eCodecOpmode,//slave(AP is master), master(AP is slave)
							SerialDataFormat eFormat,//i2s
							UINT32 uSampleRate,
							UINT8 uBitsPerSample,
							I2S_RFSLength	eMasterRFS,//when codec is master
							UINT8 uVol)
{

	UINT32 uMasterRFSBFS, uRfs, uBfs;//lsb 5bits
	UINT32  uBitsFormat, uBitSpl, uFmt = 0;//lsb 6bits
	UINT32 uR16;

return; // added by terry 20111002 for wm8976/
    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] ++WM8580_CodecInitPCMOutwithVol()\n\r")));

	if ( eCodecOpmode == I2S_Master) {
		uRfs= (eMasterRFS == RFS_256fs)? 2 : 
			  (eMasterRFS == RFS_384fs)? 3 :
			  (eMasterRFS == RFS_512fs)? 4 :
								  5;//768fs

		uBfs = 1;//32fs
		uMasterRFSBFS= (uBfs<<3 |uRfs );
	}

	if (eFormat == I2SFormat) 		uFmt = 2;
	else if (eFormat == MSBJustified) 	uFmt = 1;
	else if (eFormat == LSBJustified) 	uFmt = 0;
	else if (eFormat == AFTER_PCMSYNC_HIGH) 	uFmt = (1<<5)|3; //DSP B
	else if (eFormat == DURING_PCMSYNC_HIGH) uFmt = (1<<5)|(1<<4)|3; 	//DSP A [5]bit clock inversion, [4] pcm mode [1:0] select pcm
	uBitSpl = (uBitsPerSample==16 ||uBitsPerSample==8)? 0 : 
			(uBitsPerSample==24)? 2 : 0;
	uBitsFormat = (uBitSpl<<2)|(uFmt<<0);

	uR16= (eFormat == DURING_PCMSYNC_HIGH)?0x005://Left & Right DAC Souce : Left Channel
		    (eFormat == AFTER_PCMSYNC_HIGH) ?0x005://Left & Right DAC Souce : Left Channel
											0x009;//Left DAC : Left Channel, Right DAC : Right Channel				

	CodecMuteControl(DMA_CH_OUT, TRUE);

	if ( eCodecPort == PRIMARY_PORT )
	{
		if ( eCodecOpmode == I2S_Slave)//Master : AP chip	Slave : WM8580
		{
			if(uSampleRate==0) 	
			{
                                WM8580_CodecPrimOutSlaveNoPLL(uBitsFormat, uR16, uVol);
			}
			else if(uSampleRate==48000) 
			{
			//	WM8580_CodecPrimOutSlaveNoPLL(uBitsFormat, uR16, uVol);
			}
		}	
		else if ( eCodecOpmode == I2S_Master)	// Master : WM8580 	Slave : AP chip
		{
			if(uSampleRate==0) 	
			{
				WM8580_CodecPrimOutMasterNoPLL(uMasterRFSBFS, uBitsFormat, uR16, uVol);
			}
			else if(uSampleRate==48000) 
			{
			//	WM8580_CodecPrimOutMasterPLL_OSC12MHz_48000Hz(uMasterRFSBFS, uBitsFormat, uR16, uVol);
			}	
			else if(uSampleRate==44100) //WM8580_CodecPrimOutMasterPLL_12MHz_44100Hz(uMasterRFSBFS, uBitsFormat, uR16, uVol);
			{
			//	WM8580_CodecPrimOutMasterPLL_24MHz_44100Hz(uMasterRFSBFS, uBitsFormat, uR16, uVol);
			}
		}
	}
	else if ( eCodecPort == SECONDARY_PORT )
	{
		if ( eCodecOpmode == I2S_Slave)//Master : AP chip	Slave : WM8580
		{
		//	WM8580_CodecSecOutSlaveNoPLL(uBitsFormat, uR16, uVol);
		} else if ( eCodecOpmode == I2S_Master)// Master : WM8580 	Slave : AP chip
		{
		//	WM8580_CodecSecOutMasterNoPLL(uMasterRFSBFS, uBitsFormat, uR16, uVol);
		}
	}

	CodecMuteControl(DMA_CH_OUT, FALSE);

    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] --WM8580_CodecInitPCMOutwithVol()\n\r")));	
}

void 
HardwareContext::WM8580_CodecPrimOutSlaveNoPLL(UINT32 uBitsFormat, UINT32 uR16,  UINT8 uVol)
{
	WriteCodecRegister(0x35, 0x000);//53					//Reset WM8580
	WriteCodecRegister(0x32, 0x022);//50					//Power Down 1: ADC Disable, DACs Enable, Digital Interface Enable 
	WriteCodecRegister(0x08, 0x030);//08					//CLKSEL : All ADC, DAC & SPDIF CLk source are MCLK		
	WriteCodecRegister(0x09, 0);//09;						//PAIF 1	
	WriteCodecRegister(0x0a, 0);//10						//PAIF 2		
	WriteCodecRegister(0x16, 0x089);//22					//IZD mute detection enable, all dac zero flag on, left=left, right=right
	WriteCodecRegister(0x0c, (3<<7)|uBitsFormat);//12		//PAIF 3	CODEC Pri SDI format
	WriteCodecRegister(0x10, uR16);//16						//DAC Control 2			Left , Right DAC Souce 
	WriteCodecRegister(0x1c, 1<<8| uVol);//28  				//volume control
}


void 
HardwareContext::WM8580_CodecPrimOutMasterNoPLL(UINT32 uMasterRFSBFS, UINT32  uBitsFormat, UINT32 uR16, UINT8 uVol)
{
    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] ++WM8580_CodecPrimOutMasterNoPLL()\n\r")));

	WriteCodecRegister(0x35, 0x000);
	WriteCodecRegister(0x32, 0x022);
	WriteCodecRegister(0x09, (1<<5|uMasterRFSBFS) );
	WriteCodecRegister(0x0c, (3<<7)|uBitsFormat);
	WriteCodecRegister(0x10, uR16);
	WriteCodecRegister(0x1c, 1<<8| uVol);

	DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] --WM8580_CodecPrimOutMasterNoPLL()\n\r")));
}

#if 1


void HardwareContext::I2S_Init8580Driver()
{
    int i;
    static BOOL bIsInit = TRUE;

    // Initialize WM8580_SFR_Table data
    for (i=0; i<WM8580_MAX_REGISTER_COUNT; i++)
    {
        m_WM8580_SFR_Table[i] = 0;
    }

    // Default setting value
    for(i=0; i<(sizeof(WM8580_Codec_Init_Table)/sizeof(unsigned int)/2); i++)
    {
        WriteCodecRegister(WM8580_Codec_Init_Table[i][0], WM8580_Codec_Init_Table[i][1]);
   }

//out 3 to speak
//	for(i=0; i<(sizeof(WM8580_Codec_OpenOUT1_OUT2_Table)/sizeof(unsigned int)/2); i++)
	{
//		WriteCodecRegister(WM8580_Codec_OpenOUT1_OUT2_Table[i][0], WM8580_Codec_OpenOUT1_OUT2_Table[i][1]);
	}

//    for(i=0; i<(sizeof(WM8580_Codec_Init_Table)/sizeof(unsigned int)/2); i++)
 //   {
//        WM8580_Codec_Init_Table[i][1] = ReadCodecRegister(WM8580_Codec_Init_Table[i][0]);
 //  }
	
}

#else


void 
HardwareContext::I2S_Init8580Driver()

{
    int i;

    DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] ++I2S_Init8580Driver()\n\r")));

    // Initialize WM8580_SFR_Table data

    for (i=0; i<WM8580_MAX_REGISTER_COUNT; i++)
    {
        m_WM8580_SFR_Table[i] = 0;
    }

    if (m_bEnableCodecMaster)
    {
	WM8580_CodecInitPCMOutwithVol(PRIMARY_PORT, I2S_Master, I2SFormat,   0, 16, RFS_384fs, 0xff);//SampleRate-0
    }
    else
    {
	WM8580_CodecInitPCMOutwithVol(PRIMARY_PORT, I2S_Slave, I2SFormat,   0, 16, RFS_NA, 0xff);//SampleRate-0
    }
	DEBUGMSG(ZONE_FUNCTION,(TEXT("[WAV] --I2S_Init8580Driver()\n\r")));

}
#endif
