/*************************************************************************
Copyright(c) 2004-2006 Analog Devices, Inc. All Rights Reserved.

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.

File Name: adi_graph_driver.c 

Description - This source file is part of 'adi_graph' library.  It implements 
some APIs that are declared in 'adi_graph_driver.h'  See readme file for details.

*********************************************************************************/


#include <sys/exception.h>

#if defined(__ADSPBF533__)
#include <cdefBF533.h>
#endif
#if defined(__ADSPBF561__)
#include <cdefBF561.h>
#endif
#if defined(__ADSPBF537__)
#include <cdefBF537.h>
#endif

#include "adi_graph_driver.h"
#include "adi_itu656.h"


/*
NOTE:
Writing a single "pixel" will in fact write to both the even and odd frames
of the interlaced display, to prevent screen flicker.  This behavior can be
changed by modifying this file, and rebuilding the adi_graph library.
*/
 
//#if 0
#define WriteDestByte(val) {*dest++=val;}
//#endif
#if 0
#include <stdio.h>
#define WriteDestByte(val) \
{\
	*dest=val;\
	if ( *dest != *(dest-0x00400000) )\
#ifdef PRINT_TO_CONSOLE	
	{\
		printf ( "At source line %d (NTSC line %d): [%x] != [%x] (%x %x)\n", __LINE__, nLineNum, (dest-0x00400000), dest, (unsigned char) *(dest-0x00400000), (unsigned char) *dest );\
		exit ( 1 );\
	}\
#endif	
	dest++;\
}
#endif

void *g_pVideoBufferBaseAddress;

void 	SetVideoBufferBaseAddress ( void *addr ) 
{ 
    g_pVideoBufferBaseAddress = addr; 
}

void	*GetVideoBufferBaseAddress (void) 
{ 
    return g_pVideoBufferBaseAddress; 
}


typedef struct
{
	unsigned int SAV;
	unsigned int EAV;
} SystemCodeType;

const SystemCodeType SystemCodeMap[4] =
{
	{ 0xFF000080, 0xFF00009D },
	{ 0xFF0000AB, 0xFF0000B6 },
	{ 0xFF0000C7, 0xFF0000DA },
	{ 0xFF0000EC, 0xFF0000F1 }
};

int IsVBlankLine ( const int nLine )
{
	/* This array contains a single bit for each line in
	   an NTSC frame. */
	   
	if (	( nLine <= 18 ) ||
			( nLine >=264 && nLine <= 281 ) ||
			( nLine == 528 ) )
		return true;
		
	return false;
}

int ADI_GRAPH_DRV_InitScreenBuffer ( unsigned char *base_address )
{
	const int nNumNTSCVoutFrames = 2;
	const int nNumNTSCLines = NTSC_HEIGHT;
	unsigned char *dest = base_address;
	
	SetVideoBufferBaseAddress ( base_address );
	
	int nFrameNum, nLineNum;
	
	for ( nFrameNum = 0; nFrameNum < nNumNTSCVoutFrames; ++nFrameNum )
	{
		for ( nLineNum = 1; nLineNum <= nNumNTSCLines; ++nLineNum )
		{
			int offset = 0;
			int i;
			
			if ( IsVBlankLine ( nLineNum ) )
				offset ++;
				
			if ( nLineNum > 266 || nLineNum < 3 )
				offset += 2;
				
			/* Output EAV code */
			unsigned int code = SystemCodeMap[ offset ].EAV;
			WriteDestByte ( (char) (code >> 24) & 0xff );
			WriteDestByte ( (char) (code >> 16) & 0xff );
			WriteDestByte ( (char) (code >> 8) & 0xff );
			WriteDestByte ( (char) (code) & 0xff );
			
			/* Output horizontal blanking */
			for ( i = 0; i < 67*2; ++i )
			{
				WriteDestByte ( 0x80 );
				WriteDestByte ( 0x10 );
			}
			
			/* Output SAV */
			code = SystemCodeMap[ offset ].SAV;
			WriteDestByte ( (char) (code >> 24) & 0xff );
			WriteDestByte ( (char) (code >> 16) & 0xff );
			WriteDestByte ( (char) (code >> 8) & 0xff );
			WriteDestByte ( (char) (code) & 0xff );
		
			/* Output empty horizontal data */
			for ( i = 0; i < 360*2; ++i )
			{
				WriteDestByte ( 0x80 );
				WriteDestByte ( 0x10 );
			}
		}
	}
	
	return dest - base_address;
}

void ADI_GRAPH_DRV_WriteToCanvas ( unsigned short X, unsigned short Y, unsigned int val, void * buffer )
{
	int *pCanvas = ( int * ) buffer;

	/* Using 0xf10000ff as a marker for the beginning of an NTSC frame */
	if ( 0xf10000ff == *pCanvas )
	{
		unsigned int * base;
	
		/* Live video buffer */
		base = (unsigned int *) buffer;
	
		/* Effectively line-doubling in this routine */
		base += (unsigned int) 0x79BC/4;
	
		/* Convert from cartesian coords (Y get higher as we move up) to video addressing
		   (Y gets higher as we move down) */
		Y = (NTSC_HEIGHT/2)-Y;
		base = base + (429*Y);
		base = base + X;
		*base = val;

		/* Fill in the same pixel on the second field */
		base += (unsigned int) ( 0x75CA8/4 - 0x79BC/4 );
		*base = val;
	}
	else
	{
		/* Some other canvas.  Assuming the first eight bytes indicate the width and
		   height of the following block (which is what adi_graph "happens" to use. :) */
		int width = *pCanvas++;
		int height = *pCanvas++;

		pCanvas += ( width * Y ) + X;
		*pCanvas = val;
	}
}


unsigned int ADI_GRAPH_DRV_ReadFromCanvas (unsigned short X, unsigned short Y, void *buffer )
{
	int *pCanvas = ( int * ) buffer;
	
	/* Using 0xf10000ff as a marker for the beginning of an NTSC frame */
	if ( 0xf10000ff == *pCanvas )
	{
		unsigned int * base;
	
		/* Live video buffer */
		base = (unsigned int *) buffer;
	
		base += (unsigned int) 0x79BC/4;
	
		/* Convert from cartesian coords (Y get higher as we move up) to video addressing
		   (Y gets higher as we move down) */
		Y = (NTSC_HEIGHT/2) -Y;
		base = base + (429*Y);
		base = base + X;
		return *base;
	}
	else
	{
		/* Some other canvas.  Assuming the first eight bytes indicate the width and
		   height of the following block (which is what adi_graph "happens" to use. :) */
		int width = *pCanvas++;
		int height = *pCanvas++;

		pCanvas += ( width * Y ) + X;
		return *pCanvas;
	}
}

/********************************************************************************
	
	Convert_RGB_to_YCrCb
	
	Description: Converts R,G,B color components into a packed, YCrCb 32-bit word
				 More information on digital color can be found here:
				 http://davis.wpi.edu/~matt/courses/color/
				 
	Inputs: B - Blue color component (0-255)
			G - Green color component (0-255)
			R - Red color component
			
	Output: unsigned int containing packed YCrCb data
	
*********************************************************************************/	
unsigned int		ADI_GRAPH_DRV_ConvertRGBtoNative ( unsigned short B, unsigned short G, unsigned short R)
{
	unsigned int out;
	unsigned char Y,U,V;
	float	r,g,b,y,u,v;
	
	// scale RGB color components to fit between 0.0 and 1.0
	r = ((float) R) * (1.0/256);
	g = ((float) G) * (1.0/256);
	b = ((float) B) * (1.0/256);
	
	// convert each component to YUV
	y = 0.299 * r + 0.587 * g + 0.114 * b;
	v = ((b-y)*0.5) + 0.5;
	u = ((r-y)/1.6) + 0.5;

	// scale YUV components and cast to 8-bit values
	Y = (unsigned char) (y*255.0);
	V = (unsigned char) (v*255.0);
	U = (unsigned char) (u*255.0);
	
	// pack YUV data
	out = ((unsigned int)Y) <<24 | ((unsigned int)V) <<16 | ((unsigned int)Y) <<8 |  U;
	return out;
}

void* ADI_GRAPH_DRV_GetScreenCanvas ()
{
	return GetVideoBufferBaseAddress ();
}

