//
// MODULE  : DMPEG.C
//	PURPOSE : Entry point to lowlevel driver
//	AUTHOR  : JBS Yadawa
// CREATED :  7/20/96
//
//
//	Copyright (C) 1996 SGS-THOMSON Microelectronics
//
//
//	REVISION HISTORY :
//
//	DATE     :
//
//	COMMENTS :
//

#include "common.h"
#include "dmpeg.h"
#include "memio.h"
#include "codedma.h"
#include "sti3520A.h"
#include "staudio.h"
#include "board.h" 
#include "error.h"
#include "debug.h"
#include "i20reg.h"
#include "zac3.h"
#include "staudio.h"

#define CTRL_POWER_UP   0 // After reset
#define CTRL_INIT       1 // Initialisation + test of the decoders
#define CTRL_READY      2 // File openned
#define CTRL_INIT_SYNC  3 // Initial sync.: defines start of audio and video
#define CTRL_DECODE     4 // Normal decode
#define CTRL_IDLE       5 // idle state

#define SLOW_MODE       0
#define PLAY_MODE       1
#define FAST_MODE       2


CARD Card,  *pCard = NULL;
CTRL Ctrl,  *pCtrl;
DWORD OldlatchedSTC;
DWORD OldvideoPTS;

static BOOL Avsync;
static BOOL synchronizing;

static BYTE *tmpBuf;
static ULONG tmpBase;
static DWORD tmpAdr;

static BYTE I20Intr;
static BOOL IntCtrl(void);
static BYTE	Irq;

BOOL dmpgOpen(ULONG Base, BYTE *DmaBuf, DWORD PhysicalAddress)
{
	BOOL Found=FALSE;


    tmpBuf  = DmaBuf;
    tmpBase = Base;
    tmpAdr  = PhysicalAddress;

 	if(!BoardOpen(Base))
	{
                DPF((Trace,"Board initialization failed!!\n"));
	}
    // Open Audio and Video
	pCard = &Card;
	pCard->pVideo = &(pCard->Video);
	VideoOpen();
	pCard->pAudio = &(pCard->Audio);
	AudioOpen (pCard->pAudio);
	pCtrl = &Ctrl;
	pCtrl->ErrorMsg = NO_ERROR;
	pCtrl->CtrlState = CTRL_POWER_UP;
	pCtrl->AudioOn = FALSE;
	pCtrl->VideoOn = FALSE;
	Avsync = FALSE;

	BoardDisableIRQ();
	VideoInitDecoder(DUAL_PES);// Initialise video in PES by default
	pCtrl->CtrlState = CTRL_INIT_SYNC;
    VideoInitPesParser(VIDEO_STREAM);
    InitAC3Decoder();
    AudioInitPesParser(AUDIO_STREAM);
	BoardEnableIRQ();
	VideoMaskInt ();	   // Restore Video interrupt mask
    DPF((Trace,"Calling Code DMA open!!\n"));
    return CodeDmaOpen(DmaBuf, PhysicalAddress);
}

BOOL dmpgClose(void)
{
        DPF((Trace,"dmpgClose!!\n"));
	if(pCard == NULL)
		return FALSE;
	VideoClose( );
	AudioClose( );
	BoardClose();
	CodeDmaClose();
	return TRUE;
	
}


BOOL dmpgPlay()
{
        DPF((Trace,"dmpgPlay!!\n"));
	if(pCard == NULL)
		return FALSE;

	VideoRestoreInt ();	   // Restore Video interrupt mask
	pCtrl->DecodeMode = PLAY_MODE;
    AudioSetMode(pCtrl->DecodeMode, 0);
	VideoSetMode(pCtrl->DecodeMode, 0);
	if (pCtrl->CtrlState == CTRL_IDLE)
		pCtrl->CtrlState = CTRL_DECODE;
	VideoDecode();
    AudioDecode();
	AC3Command(AC3_UNMUTE); //ckw
	AC3Command(AC3_PLAY); //ckw
	AudioTransfer(TRUE); //ckw

	return TRUE;

}

void dmpgReset(void)
{
        dmpgPause();
        dmpgSeek();
        VideoMaskInt();
}

BOOL    dmpgPause(void)
{
        DPF((Trace,"dmpgPause!!\n"));

	if(pCard == NULL)
		return FALSE;
	switch(pCtrl->CtrlState) 
	{
		case CTRL_POWER_UP:
		case CTRL_INIT:
		case CTRL_READY:
			break;
		case CTRL_INIT_SYNC:
			AudioPause();
			VideoPause();
                        AudioTransfer(FALSE); //ckw
                        AC3Command(AC3_MUTE); //ckw
			pCtrl->AudioOn = FALSE;
			pCtrl->VideoOn = FALSE;
			pCtrl->ActiveState = CTRL_INIT_SYNC;
			pCtrl->CtrlState = CTRL_IDLE;
			break;
	case CTRL_DECODE:
			AudioPause();
			VideoPause();
                        AudioTransfer(FALSE); //ckw
                        AC3Command(AC3_MUTE); //ckw

			pCtrl->ActiveState = CTRL_DECODE;
			pCtrl->CtrlState = CTRL_IDLE;
			break;
	case CTRL_IDLE:
		break;
	}

	return TRUE;
}

BOOL    dmpgSeek(void)
{

        DPF((Trace,"dmpgSeek!!\n"));
	if(pCard == NULL)
		return FALSE;
		
	CodeDmaFlush();
	pCtrl->CtrlState = CTRL_INIT_SYNC;
	VideoSeekDecoder(DUAL_PES);
	return TRUE;
}

BOOL    dmpgStop(void)
{

    DPF((Trace,"dmpgStop!!\n"));
	if(pCard == NULL)
		return FALSE;
    VideoMaskInt();
    dmpgPause();
//    dmpgClose();
//    dmpgOpen(tmpBase, tmpBuf, tmpAdr);

	return TRUE;
}

UINT dmpgSendVideo(BYTE *pPacket, DWORD uLen)
{
//        DPF((Trace,"dmpgSendVideo!!\n"));
        UINT uRet;

	if(pCard == NULL)
		return FALSE;                                 
    
    
        uRet = (UINT)CodeDmaSendData(pPacket, uLen);

        return uRet;
}

UINT dmpgSendAudio(BYTE *pPacket, DWORD uLen)
{

        LONG abl, abf;

//        DPF((Trace,"dmpgSendAudio!!\n"));
    	if(pCard == NULL)
                return 0;
        abl = VideoGetABL();
        if((LONG)pCard->pVideo->AudioBufferSize - abl <= 2L)
        {
//            DebugPrint((DebugLevelTrace, "Audio Buffer FULL!!\n"));
            return 0;
        }

        abf = (pCard->pVideo->AudioBufferSize - abl -1) << 8;
//        DebugPrint((DebugLevelError,"SendAudio : %X %X %X %X %X %X, BBL = %x Size = %x\n",pPacket[0], pPacket[1], pPacket[2], pPacket[3], pPacket[4], pPacket[5], abl, pCard->pVideo->AudioBufferSize));
        if(abf < uLen)
        {
                SendAC3Data(pPacket, abf);
                return abf;
        }
        else
        {
                SendAC3Data(pPacket, uLen);
                return uLen;
        }
}

BOOL dmpgInterrupt(void)
{
	BOOL bRet = FALSE;

	I20Intr = memInByte(I20_INTRSTATUS);	
	memOutByte(I20_INTRSTATUS, I20Intr);

	if(I20Intr & IFLAG_CODEDMA)
	{   
		bRet = TRUE;
		CodeDmaInterrupt();
	}

	if(I20Intr & IFLAG_GIRQ1)
	{
		bRet = TRUE;
		IntCtrl();
	}
	return bRet;
}

void dmpgEnableIRQ()
{
   DPF((Trace,"IrqEnabled!!\n"));
   VideoRestoreInt();
   BoardEnableIRQ();
}

void dmpgDisableIRQ()
{
   DPF((Trace,"IrqDisabled!!\n"));
   VideoMaskInt();
   BoardDisableIRQ();
}

void CtrlInitSync(void)
{
	switch (pCtrl->CtrlState) {
	case CTRL_INIT_SYNC:
		/*
		Initial synchronisation: the video decoder is always started.
		In case of system stream, the STC is initialised in interrupt with first
		video PTS available the video state becomes VIDEO_DECODE
		When the STC becomes equal (or higher) than the first Audio PTS the audio
		decoding can be enabled
		In case of Audio only bitstreams the video is stopped and audio enabled
		In case of Video only bitstreams, the video decoding continues while
		audio is disabled
		*/
		if (VideoGetState() == StateDecode) {
			// the STC is initialized in interrupt with (first video PTS - 3 fields)
			DWORD             Stc;
			DWORD             PtsAudio;
			Stc = AudioGetSTC();
			if (AudioIsFirstPTS()) {
				PtsAudio = AudioGetPTS();
//				if (Stc >= PtsAudio)
				{
//					AudioDecode();
					pCtrl->CtrlState = CTRL_DECODE;
				}
			}
		}
		pCtrl->ErrorMsg = VideoGetErrorMsg();
		break;

	case CTRL_DECODE:	// Audio and Video decoders are running
		if (pCtrl->ErrorMsg == NO_ERROR)
			pCtrl->ErrorMsg = VideoGetErrorMsg();
		if (pCtrl->ErrorMsg == NO_ERROR)
			pCtrl->ErrorMsg = AudioGetErrorMsg();
		break;

	default :
		break;
	}
}


static BOOL IntCtrl(void)
{
	long     diff;
	WORD			lattency;
	BOOL	STIntr = FALSE;

	BoardEnterInterrupt();
	VideoMaskInt();
//	AudioMaskInt();

//	STIntr|=AudioAudioInt();
	STIntr = VideoVideoInt();

	if (Avsync) {
		CtrlInitSync();
		if (VideoIsFirstDTS()) {
			// True only on the start of decode of the first picture with an assoicated valid PTS
			DWORD FirstStc;

			FirstStc = VideoGetFirstDTS();
			AudioInitSTC(FirstStc);
			synchronizing = 0;
		}

		if (AudioGetState() == AUDIO_DECODE) {
			if (VideoIsFirstField()) {
				synchronizing++;
				if (synchronizing >= 20)
				{
					// We must verify the synchronisation of the video on the audio STC
					DWORD latchedSTC, videoPTS;
					WORD mode;

					latchedSTC = AudioGetVideoSTC();
					videoPTS = VideoGetPTS();
					mode =pCard->pVideo->StreamInfo.displayMode;
					lattency = 2200;	 /* 1.5 field duration for 60 Hz video */
					if (mode == 0) {
						lattency = 2700; /* 1.5 field duration for 50 Hz video */
					}
					diff = latchedSTC - videoPTS;
					OldlatchedSTC = latchedSTC;
					OldvideoPTS = videoPTS;

					//---- If desynchronized
					if (labs(diff) > (long)lattency) {
						if (diff < 0)
						{
							VideoRepeat ();
						}
						else
						{
							VideoSkip ();
						}
					}
					synchronizing = 0;
				}
			}
		}
	}

	BoardLeaveInterrupt();
	VideoRestoreInt();
//	AudioRestoreInt();
	return STIntr;
}