/*****************************************************************************

Copyright (c) 2006 Analog Devices, Inc.	All	Rights Reserved. 

This software is proprietary and confidential to Analog	Devices, Inc. 
and	its	licensors.

*****************************************************************************

$RCSfile: JPEGImageEdgeDetection.c,v $
$Revision: 1.2 $
$Date: 2007/05/23 02:29:18 $

Project:	BlackfinSDK (JPEG Image Edge Detection)
Title:		JPEG Image Edge Detection
Author(s):	bmk
Revised	by:	

Description:
			Functions to perform Edge Detection on JPEG Image

References:
			None

*****************************************************************************
Tab	Setting:			4

Target Processor:		ADSP-BF5xx
Target Tools Revision:	ADSP VisualDSP++ v4.5
******************************************************************************

Modification History:
====================
$Log: JPEGImageEdgeDetection.c,v $
Revision 1.2  2007/05/23 02:29:18  randreol
Changes in ssl_init and VDSP5.0 porting

Revision 1.4  2006/11/20 05:56:50  bmk
Merged BF533 & BF561
Fixed BF561 cache issue

Revision 1.3  2006/11/03 07:11:21  bmk
SDK 2.0  files - Initial Entry

Revision 1.1  2006/07/25 08:09:08  bmk
initial entry


******************************************************************************
Include files
*****************************************************************************/

#include <sobel.h>							// Sobel edge detection algorithm includes
#include <JPEGImageEdgeDetection_System.h>	// system includes

/*****************************************************************************
Video Data buffers
*****************************************************************************/

// Sobel Edge detection buffers
section("Sobel_In_buf") 	volatile u8 SobelInBuf	[SOBEL_BUF_SIZE];
section("Sobel_Out_buf") 	volatile u8 SobelOutBuf	[SOBEL_BUF_SIZE];

/***********************************
MDMA Handels & data
***********************************/

// DMA Stream Handels for Edge Detection
ADI_DMA_STREAM_HANDLE		MDMAStreamHandle;
// MDMA 2D transfer data type
ADI_DMA_2D_TRANSFER			MDMA_SobelInSrc;
ADI_DMA_2D_TRANSFER			MDMA_SobelInDes;
ADI_DMA_2D_TRANSFER			MDMA_SobelOutSrc;
ADI_DMA_2D_TRANSFER			MDMA_SobelOutDes;

/***********************************
Globals
***********************************/

// Background colour for edge detected output (Grey colour - UYVY format)
static char EdgeDetectBackground[]	= {0x89,0x11,0x89,0x11};

// Pointer to a location in JPEG decoded buffer - points next block of data for Sobel_In_Bufx
u8	*pVideoInBuf;		
// Pointer to a location in EdgeDetectBuf - points start address to copy next block of Sobel_Out_Buf
u8	*pVideoOutBuf;
// Temp video in pointer
u8	*tpVideoInBuf;
// Temp video out pointer
u8	*tpVideoOutBuf;
// flag to indicate Edge Detection MDMA status
volatile bool	MDMALock;

u8	SobelInBlockCount;			// Number of sobel in blocks processed
u8	SobelOutBlockCount;			// Number of sobel out blocks processed
u8	SobelInColumnBlockCount;	// Number of sobel in Columns processed
u8	SobelOutColumnBlockCount;	// Number of sobel out Columns processed
	
/***********************************
local function prototypes
***********************************/
// Configures & Installs Memory DMA for Edge Detection
void 		ConfigMDMA_EdgeDetection(void);
// MEMDMA stream callback function (sobel in)
static void MDMACallbackSobelIn		(void *AppHandle, u32  Event, void *pArg);	
// MEMDMA stream callback function (sobel out)
static void MDMACallbackSobelOut	(void *AppHandle, u32  Event, void *pArg);
// MDMA Sobel In buffer update
void		MDMASobelIn				(void);
// MDMA Sobel Out buffer update
void		MDMASobelOut			(void);
// Performs Sobel Edge detection on selected Video Frame
void 		do_EdgeDetection		(void);
//Formats Edge detected output buffer to specified ITU656 frame
void		FormatEdgeDetectFrame	(void);

/*********************************************************************

    Function:       KickOffEdgeDetection

    Description:    Kicks off (Sobel) Edge Detection

*********************************************************************/
section("sdram0_bank1_cache")
void KickOffEdgeDetection (void)
{
	
    // Format Edge detect output frame to selected ITU656 video mode
    FormatEdgeDetectFrame ();
       
	/***********************************************
    Initialise Video In/Out buffer address pointers

    Video In Start address - init to point first 
    luma value minus 2 of Video In Buffer Active Field1
    to account for sobel algorithim invaild data, 
    pass extra 2 rows per block & 2 columns per row
    
    Video Out Start address - init to point first 
    luma value of EdgeDetectBuf Active Field1
    ***********************************************/
	
    // Start address - init to point the first luma value minus 2 of Video In Buffer Active Field1
	pVideoInBuf		= (u8 *)(pJPEGImageBuf + (Field1Skip + ActiveVideoSkip - (DataPerLine + ITU_Y_OFFSET)));	
	
	// Video out start address - init to point the first luma value of EdgeDetectBuf Active Field1
	pVideoOutBuf	= (u8 *)(pEdgeDetectBuf + (Field1Skip + ActiveVideoSkip + ITU_Y_OFFSET));		
				
	tpVideoInBuf 	= NULL;			// clear the temp video in buffer pointer
	tpVideoOutBuf 	= NULL;			// clear the temp video out buffer pointer

    // wait until shared MDMA flag is cleared (MDMA shared by YUV to ITU656 frame conversion & Edge detection)
    while (MDMASharedFlag);
    
    // Configure MDMA for edge Detection
    ConfigMDMA_EdgeDetection ();
    
    MDMASharedFlag	= TRUE;	// Reserve the MDMA stream for Edge Detection
    	
	/***********************************************
	Init Sobel Buffer management Flags & Counters
	***********************************************/
	
	SobelInBlockCount			= 0;		// sobel buf sized block (Block 0 of Active frame 1) will be read from Video in buffer    
	SobelInColumnBlockCount		= 1;		// Processing Column 1 sobel blocks
	SobelOutBlockCount			= 0;		// Block 0 of Active frame 1 - will be copied to Edge Detect buffer after edge detection
	SobelOutColumnBlockCount 	= 1;		// Processing Column 1 sobel blocks
	MDMALock					= FALSE;	// MDMA is available for Edge detection & edge detection hasn't started yet. 
	
	// Perform edge detection on decoded JPEG image
	do_EdgeDetection ();
	
   	return;
}

/*********************************************************************

    Function:       FormatEdgeDetectFrame
    Description:    Formats Edge detected output buffer to specified
    				ITU656 frame

*********************************************************************/
section("sdram0_bank1_cache")
void	FormatEdgeDetectFrame	(void)
{

    // look for selected output video mode
	if (OutputVideoMode == ITU656_NTSC)
	{
		// Format memory as NTSC interlaced video frame
		adi_video_FrameFormat ((char *)pEdgeDetectBuf,NTSC_IL);
		// Fill the Video out frame with background color
		adi_video_FrameFill ((char *)pEdgeDetectBuf,NTSC_IL,EdgeDetectBackground);		
	}
	else if (OutputVideoMode == ITU656_PAL)
	{
		// Format memory as PAL interlaced video frame
		adi_video_FrameFormat ((char *)pEdgeDetectBuf,PAL_IL);
		// Fill the Video out frame with background color
		adi_video_FrameFill ((char *)pEdgeDetectBuf,PAL_IL,EdgeDetectBackground);	    
	}

	return;

}

/*********************************************************************

    Function:       ConfigMDMA_EdgeDetection

    Description:    Configures & Installs Memory DMA for Edge Detection

*********************************************************************/
section("sdram0_bank1_cache")
void ConfigMDMA_EdgeDetection	(void) 
{

    // Configure MDMA 2D structures

    /*******************************************
    video in buffer to sobel in buffer stream
    *******************************************/

	/**** Sobel In stream destination buffer config ****/
	
    // SobelInBuf has luma values for edge detection
    MDMA_SobelInDes.StartAddress	= (void *) (&SobelInBuf[0]);    			
    // count to copy a block of size (SOBEL_COLUMN_SIZE+SOBEL_OFFSET) * (SobelRowSize+SOBEL_OFFSET) 
	MDMA_SobelInDes.XCount			= (SOBEL_COLUMN_SIZE+SOBEL_OFFSET);
	// Copy luma values only
	MDMA_SobelInDes.XModify			= 1;	// 1 byte increment
	// count to copy a block of size (SOBEL_COLUMN_SIZE+SOBEL_OFFSET) * (SobelRowSize+SOBEL_OFFSET)
	MDMA_SobelInDes.YCount			= (SobelRowSize+SOBEL_OFFSET);
	// Move to next row
	MDMA_SobelInDes.YModify			= 1;	// same as xmodify
    	
    /**** Sobel In stream source buffer config ****/    	    
    
	// count to copy a block of size (SOBEL_COLUMN_SIZE+SOBEL_OFFSET) * (SobelRowSize+SOBEL_OFFSET)
	MDMA_SobelInSrc.XCount			= (SOBEL_COLUMN_SIZE+SOBEL_OFFSET);
	// Copy luma values only
	MDMA_SobelInSrc.XModify			= 2;	// 2 byte increment (alternative data skipped)
	// count to copy a block of size (SOBEL_COLUMN_SIZE+SOBEL_OFFSET) * (SobelRowSize+SOBEL_OFFSET)
	MDMA_SobelInSrc.YCount			= (SobelRowSize+SOBEL_OFFSET);
	// Move to next row, point to first luma location minus 2
	MDMA_SobelInSrc.YModify			= (DataPerLine - ((SOBEL_COLUMN_SIZE+SOBEL_OFFSET) * 2) + 2);	// finally add xmodify		

    /*******************************************
    sobel out buffer to video out buffer stream
    *******************************************/

    /**** Sobel Out stream destination buffer config ****/

	// count to copy sobel out valid data only
	MDMA_SobelOutDes.XCount			= SOBEL_COLUMN_SIZE;
	// Copy valid data to luma locations only
	MDMA_SobelOutDes.XModify		= 2;
	// count to copy sobel out valid data only
	MDMA_SobelOutDes.YCount			= SobelRowSize;
	// Move to next row, point to first luma location
	MDMA_SobelOutDes.YModify		= (DataPerLine - (SOBEL_COLUMN_SIZE * 2) + 2);	// finally add xmodify
        
    /**** Sobel Out stream source buffer config *****/

 	// SobelOutBuf has sobel edge detected values
	MDMA_SobelOutSrc.StartAddress	= (void *) (&SobelOutBuf[0] + SOBEL_COLUMN_SIZE + SOBEL_OFFSET + 1);			
	// count to copy sobel out valid data only
	MDMA_SobelOutSrc.XCount			= SOBEL_COLUMN_SIZE;
	// Copy luma values only
	MDMA_SobelOutSrc.XModify		= 1;
	// count to copy sobel out valid data only
	MDMA_SobelOutSrc.YCount			= SobelRowSize;
	// Move to next row
	MDMA_SobelOutSrc.YModify		= 3;	// skip last column data in present row and first column data in next row + xmodify
	   
	/*********************************************************
    Install MDMA for Edge detection
    *********************************************************/
    
    // Open MDMA
#if defined(__ADSPBF533__)
   	// DMA Manager Handle, DMA Controller - MDMA stream 1, Client Handle, Stream Handle, live callback
   	// MDMA Stream 0 is allocated to USB device. MDMA Stream 1 is shared by JPEG decoding & Edge Detection
   	ezErrorCheck(adi_dma_MemoryOpen (adi_dma_ManagerHandle,ADI_DMA_MDMA_1,NULL,&MDMAStreamHandle,NULL));
#elif defined(__ADSPBF561__)
   	// DMA Manager Handle, DMA Controller 1 - MDMA stream 1, Client Handle, Stream Handle, live callback
   	// DMA controller 1 - MDMA Stream 0 is allocated to USB device & MDMA Stream 1 is shared by JPEG decoding
   	ezErrorCheck(adi_dma_MemoryOpen (adi_dma_ManagerHandle,ADI_DMA_MDMA1_1,NULL,&MDMAStreamHandle,NULL));
#endif

	return;
}

/*********************************************************************

    Function:       do_EdgeDetection

    Description:    Performs (sobel) edge detection on a Video frame

*********************************************************************/
section ("App_Code_L1")
void do_EdgeDetection (void)
{
    // start next sobel conversion only if this falg is cleared
	while (!SobelDone)
    {   
       	// load next set of luma values from Video In Buffer to SobelInBuf
		MDMASobelIn ();
					
		// wait until above sobel MDMA transfer is done
		while (MDMALock);
			
		// call Sobel edge detection routine
		sobel_fast(&SobelInBuf[0], (SobelRowSize+SOBEL_OFFSET), (SOBEL_COLUMN_SIZE+SOBEL_OFFSET), &SobelOutBuf[0], 2);

      	// Copy SobelOutBuf to EdgeDetectBuf streaming
		MDMASobelOut();
		
		// wait until previous sobel MDMA transfer is done
		while (MDMALock);		
		
	}
	
	// Close MDMA stream used for edge detection
    ezErrorCheck(adi_dma_MemoryClose (MDMAStreamHandle,TRUE)); 
    
	MDMASharedFlag	= FALSE;    // Release MDMA
	
	return;
}

/*********************************************************************

    Function:       MDMASobelIn

    Description:    MDMA Sobel In buffer update

*********************************************************************/
section ("App_Code_L1")
void MDMASobelIn(void)
{
   	
	/*******************************************
    video in buffer to sobel in buffer stream
    *******************************************/
    
	/**** Sobel In stream destination buffer config ****/
	++SobelInBlockCount;	// next sobel block number to build

	// Check if this is the first sobel block
    // if yes, load the pVideoInBuf value
    if (tpVideoInBuf == NULL)	
    {
    	tpVideoInBuf = pVideoInBuf;
    	SobelInBlockCount = 0;	// Sobel is processing block 0
    }
	// Check if Active Field 1 is done. if so, process Video In Buffer Active field 2
    else if (!(SobelInBlockCount % SobelBlockCount))
    {
        // pVideoInBuf points to first luma value minus 2 of Video In Buffer Active Field1 
        // with sobel algorithm offset of extra 2 rows per block & 2 columns per row
        
        // update pVideoInBuf & temp pointer to point to first luma value of the above buffer's Active Field2
        // with sobel algorithm offset of extra 2 rows per block & 2 columns per row
        pVideoInBuf				= (pVideoInBuf + (Field2Skip - Field1Skip));	
        tpVideoInBuf 			= pVideoInBuf;
        SobelInColumnBlockCount = 1;	// move to column 1 of sobel block
    }    
    else if (!(SobelInBlockCount % SobelFieldBlocks))		// move to the next column block
    {      
        // pVideoInBuf always to first luma value of Video In Buffer Active Field1 or Field2
        // with sobel algorithm offset of extra 2 rows per block & 2 columns per row
        
        tpVideoInBuf = (pVideoInBuf + (SOBEL_COLUMN_SIZE * 2 * SobelInColumnBlockCount));
        SobelInColumnBlockCount++;			// move to next column in the sobel block        
    }
    else		// move to next row in sobel block
    {
        tpVideoInBuf = (tpVideoInBuf + (DataPerLine * SobelRowSize));
    }
                	    
    // Load the Source start address
	MDMA_SobelInSrc.StartAddress 	= (void *) tpVideoInBuf;

	// lock Edge Detection MDMA until this transfer is finished		
	MDMALock	= TRUE;
	
   	// Start MDMA 2D copy (video in buffer to sobel in buffer stream)
   	// MDMA stream handle, 2D destination config, 2D source config, Element width (1 byte), Callback function
   	ezErrorCheck(adi_dma_MemoryCopy2D (MDMAStreamHandle,&MDMA_SobelInDes,&MDMA_SobelInSrc,1,MDMACallbackSobelIn));	
   	
   	return;
}

/*********************************************************************

    Function:       MDMACallbackSobelIn

    Description:    Memory DMA callback dunction (sobel in)

*********************************************************************/
section ("Callback_Code")
static void MDMACallbackSobelIn( 
	void    *AppHandle,
	u32     Event,
	void    *pArg   
){
    // Case of (event type)
    switch (Event) {
        
        // Case descriptor processed 
        case ADI_DMA_EVENT_DESCRIPTOR_PROCESSED:

        	MDMALock = FALSE;	// clear MDMA lock flag
        	               	
        	break;
                     
        // Case DMA Error
        case ADI_DEV_EVENT_DMA_ERROR_INTERRUPT:

         	// turn on all LEDs and wait for help
            ezTurnOnAllLEDs();
            while (1) ;
            
		default:	// other events
			break;

    }
}

/*********************************************************************

    Function:       MDMASobelOut

    Description:    MDMA Sobel Out buffer update - callback routine

*********************************************************************/
section ("App_Code_L1")
void MDMASobelOut()
{
    if (SobelDone)
    // Sobel conversion of Video In Buffer is done. wait until next Video In Buffer is ready
   		return;
   		
	/*******************************************
    Sobel out buffer to video out buffer stream
    *******************************************/

	/**** Sobel Out stream Source buffer config ****/	
	// Start address - init to point the first VALID location in Sobel_Out_Buf1
    // always first and last row in a sobel out buffer is invalid
    // always first and last column in each sobel out buffer row is invalid
   
	++SobelOutBlockCount;		// next sobel block number to build
    
    // Check if this is the first sobel block
    // if yes, load the pVideoOutBuf value (pVideoOutBuf points to first luma value of Video_In_Bufx Active Field1)
    if (tpVideoOutBuf == NULL)	
    {
    	tpVideoOutBuf = pVideoOutBuf;
    	SobelOutBlockCount = 0;	// Sobel is processing block 0
    }
    // Check if Active Field 1 is done. 
   	else if (!(SobelOutBlockCount % SobelBlockCount))	// if yes, process Video_In_Bufx Active field 2
    {
       	// pVideoOutBuf is pointing to first luma value of Video_In_Bufx Active Field1 
      
       	// update pVideoOutBuf & temp pointer to point to first luma value of the above buffer's Active Field2
       	pVideoOutBuf 	= (pVideoOutBuf + (Field2Skip - Field1Skip));
       	tpVideoOutBuf 	= pVideoOutBuf;
       	SobelOutColumnBlockCount = 1;	// move to column 1 of sobel block
    }
    else if (!(SobelOutBlockCount % SobelFieldBlocks))		// move to the next column block
    {       
        // pVideoOutBuf points to first luma value of Video_In_Bufx Active Field1 or Field2
        
        tpVideoOutBuf = (pVideoOutBuf + (SOBEL_COLUMN_SIZE * 2 * SobelOutColumnBlockCount));
        SobelOutColumnBlockCount++;			// move to next column in the sobel block        
    }
    else		// move to next row in sobel block
    {
       	tpVideoOutBuf = (tpVideoOutBuf + (DataPerLine * SobelRowSize));
    }
    
    // completed processing whole video in bufx frame
    if ((SobelOutBlockCount+1) >= (SobelBlockCount * 2))
        // Sobel conversion of Video In Buffer is done. wait until next Video In Buffer is ready
       	SobelDone		= TRUE;	
               
    // Load the Destination start address
	MDMA_SobelOutDes.StartAddress		= (void *) tpVideoOutBuf;
	
	// lock Edge Detection MDMA until this transfer is finished		
	MDMALock	= TRUE;	
	
   	// Start MDMA 2D copy (Sobel Out Buf to Video Out Buf data stream)
   	// MDMA stream handle, 2D destination config, 2D source config, Element width (1 byte), Callback function
   	ezErrorCheck(adi_dma_MemoryCopy2D (MDMAStreamHandle,&MDMA_SobelOutDes,&MDMA_SobelOutSrc,1,MDMACallbackSobelOut));	
   	
   	return;
}	

/*********************************************************************

    Function:       MDMACallbackSobelOut

    Description:    Memory DMA callback function (sobel out)

*********************************************************************/
section ("Callback_Code")
static void MDMACallbackSobelOut( 
	void    *AppHandle,
	u32     Event,
	void    *pArg   
){
    // Case of (event type)
    switch (Event) {
        
        // Case descriptor processed 
        case ADI_DMA_EVENT_DESCRIPTOR_PROCESSED:
        
			MDMALock = FALSE;	// clear MDMA lock flag
			
        	break;

        // Case DMA Error
        case ADI_DEV_EVENT_DMA_ERROR_INTERRUPT:

         	// turn on all LEDs and wait for help
            ezTurnOnAllLEDs();
            while (1) ;
            
		default:	// other events
			break;
    }
}

/*****/
