/*
 *******************************************************************************
 *
 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
 * ALL RIGHTS RESERVED
 *
 *******************************************************************************
 */

/**
 *******************************************************************************
 *
 * \file edgeDetectionDSPAlgo.c
 *
 * \brief Algorithm for Alg_EdgeDetection on DSP
 *
 *        This Alg_EdgeDetection algorithm is only for demonstrative purpose.
 *        It is NOT product quality.
 *        This algorithm does a frame copy. Height and width gets decided during
 *        Create. If height / width needs to be altered, then control call
 *        needs to be done.
 *
 * \version 0.0 (Aug 2013) : [PS] First version
 *
 *******************************************************************************
 */

/*******************************************************************************
 *  INCLUDE FILES
 *******************************************************************************
 */
#include "../include/iEdgeDetectionAlgo.h"
#include <vcop/vcop.h>
#include <assert.h>
#include "inc/csl_edma.h"
#include "inc/cslr_edmacc.h"
#include "inc/eve.h"
#include "inc/tistdtypes.h"
#include "inc/eve_dma_macros.h"
#include "inc/eve_dma_funcs.h"
#include "inc/algo_dma_auto_incr.h"
#include "inc/image_dims.h"
#include "inc/vcop_memmap.h"
#include <arp32.h>

#define ALIGN_SIZE(size, num_byte) ((size+num_byte*8-1) & (0xFFFFFFFE<<num_byte))

unsigned int  gAlg_edgeDetectProfile[10];
//extern unsigned short*   __pblock_vcop_sobelX_3x3_2D_uchar_char;
extern unsigned short*   __pblock_vcop_sobelX_3x3_uchar_char;
//extern unsigned short*   __pblock_vcop_sobelY_3x3_2D_uchar_char;
extern unsigned short*   __pblock_vcop_sobelY_3x3_uchar_char;
extern unsigned short*   __pblock_vcop_sobel_magnitude;


/**
 *******************************************************************************
 *
 * \brief Implementation of create for frame copy algo
 *
 * \param  pCreateParams    [IN] Creation parameters for frame copy Algorithm
 *
 * \return  Handle to algorithm
 *
 *******************************************************************************
 */
Alg_EdgeDetection_Obj * Alg_EdgeDetectionCreate(
                        Alg_EdgeDetectionCreateParams *pCreateParams)
{

    Alg_EdgeDetection_Obj * pAlgHandle;

    pAlgHandle = (Alg_EdgeDetection_Obj *) malloc(sizeof(Alg_EdgeDetection_Obj));

    UTILS_assert(pAlgHandle != NULL);

    pAlgHandle->maxHeight   = pCreateParams->maxHeight;
    pAlgHandle->maxWidth    = pCreateParams->maxWidth;

    return pAlgHandle;
}

/**
 *******************************************************************************
 *
 * \brief Implementation of Process for frame copy algo
 *
 *        Supported formats are SYSTEM_DF_YUV422I_YUYV, SYSTEM_DF_YUV420SP_UV
 *        It is assumed that the width of the image will
 *        be multiple of 4 and buffer pointers are 32-bit aligned.
 *
 * \param  algHandle    [IN] Algorithm object handle
 * \param  inPtr[]      [IN] Array of input pointers
 *                           Index 0 - Pointer to Y data in case of YUV420SP,
 *                                   - Single pointer for YUV422IL or RGB
 *                           Index 1 - Pointer to UV data in case of YUV420SP
 * \param  outPtr[]     [IN] Array of output pointers. Indexing similar to
 *                           array of input pointers
 * \param  width        [IN] width of image
 * \param  height       [IN] height of image
 * \param  inPitch[]    [IN] Array of pitch of input image (Address offset
 *                           b.n. two  consecutive lines, interms of bytes)
 *                           Indexing similar to array of input pointers
 * \param  outPitch[]   [IN] Array of pitch of output image (Address offset
 *                           b.n. two  consecutive lines, interms of bytes)
 *                           Indexing similar to array of input pointers
 * \param  dataFormat   [IN] Different image data formats. Refer
 *                           System_VideoDataFormat
 *
 * \return  SYSTEM_LINK_STATUS_SOK on success
 *
 *******************************************************************************
 */
Int32 Alg_EdgeDetectionProcess(Alg_EdgeDetection_Obj *algHandle,
                           UInt32            *inPtr[],
                           UInt32            *outPtr[],
                           UInt32             width,
                           UInt32             height,
                           UInt32             inPitch[],
                           UInt32             outPitch[],
                           UInt32             dataFormat
                          )
{
    // Edge Detection Process Function
    VCOP_HEAP_INFO heapInfo;

    /* ping pong buffer, used as scratch buffers in internal VCOP memory */
    unsigned char *src_proc_ping;
    unsigned char *dst_proc_ping;
    signed short *inter_dat;
    char *OUTX;
    char *OUTY;
    /* ----------------------------------------------------------------- */
    /*  Declare a dma state structure                                    */
    /* ----------------------------------------------------------------- */

    DMA_State_Struct    dma_state_struct;


    unsigned int            num_param;
    unsigned int            num_channels;
    DMA_Chan_Attr           dma_attr;
    Bool                    trf_status;
    Bool                    *trf_pend;
    unsigned int            itccEn;
    unsigned int            tccEn;
    unsigned int            itcintEn;
    unsigned int            tcintEn;
    unsigned int            tcc_in;
    unsigned int            tcc_out;
    unsigned int            tccMode;
    unsigned int            fwid;
    unsigned int            stat;
    unsigned int            syncDim;
    unsigned int            dam;
    unsigned int            sam;
    unsigned int            chan_in;
    unsigned int            chan_out;

    CSL_EdmaccRegsOvly      edma_cc;

    /* ----------------------------------------------------------------- */
    /* Arrays to store event numbers, transfer complete codes            */
    /* queue numbers, and register numbers for each channel              */
    /* ----------------------------------------------------------------- */

    unsigned int            evt_no_mem[NUM_CHANNELS];
    unsigned int            tcc_no_mem[NUM_CHANNELS];
    unsigned int            que_no_mem[NUM_CHANNELS];
    unsigned int            reg_no_mem[NUM_CHANNELS];


    /* ----------------------------------------------------------------- */
    /* Option words to hold configuration of dma transfers               */
    /* ----------------------------------------------------------------- */

    unsigned int            opt_1d_2d;
    unsigned int            opt_2d_1d;


    /* ----------------------------------------------------------------- */
    /* Dimension of 2D block to be transfered and processed              */
    /* ----------------------------------------------------------------- */

    unsigned int            num_bytes = BLK_WIDTH;
    unsigned int            num_lines = BLK_HEIGHT;
    unsigned int            src_pitch = inPitch[0];
    unsigned int            dst_pitch = IMG_WIDTH;
    unsigned int            pad       = BLK_PAD;

    /* ----------------------------------------------------------------- */
    /* Pointers to input/output buffers in external memory               */
    /* ----------------------------------------------------------------- */

    unsigned char           *src;
    unsigned char           *dst;

    unsigned int            t0;
    unsigned int            t1;

    vcop_heap_reset(&heapInfo);

    src_proc_ping = vcop_heap_alloc(&heapInfo,
                                    VCOP_IBUFLA,
                                    (BLK_WIDTH + BLK_PAD) * (BLK_HEIGHT + BLK_PAD));

    dst_proc_ping = vcop_heap_alloc(&heapInfo,
                                    VCOP_IBUFHA,
                                    (BLK_WIDTH) * (BLK_HEIGHT));

    inter_dat = vcop_heap_alloc(&heapInfo,
                                VCOP_WMEM,
                                ALIGN_SIZE((BLK_WIDTH*(BLK_HEIGHT + BLK_PAD)*2),4));

    OUTX = vcop_heap_alloc(&heapInfo,
                           VCOP_WMEM,
                           ALIGN_SIZE(((BLK_WIDTH + BLK_PAD) * (BLK_HEIGHT + BLK_PAD)),4));

    OUTY = vcop_heap_alloc(&heapInfo,
                           VCOP_WMEM,
                           ALIGN_SIZE(((BLK_WIDTH + BLK_PAD) * (BLK_HEIGHT + BLK_PAD)),4));

/*
    __pblock_vcop_sobelX_3x3_2D_uchar_char = vcop_heap_alloc(&heapInfo,
                                                             VCOP_WMEM,
                                                             48);
*/
    __pblock_vcop_sobelX_3x3_uchar_char  = vcop_heap_alloc(&heapInfo,
                                                           VCOP_WMEM,
                                                           64);
/*
    __pblock_vcop_sobelY_3x3_2D_uchar_char = vcop_heap_alloc(&heapInfo,
                                                             VCOP_WMEM,
                                                             48);
*/
    __pblock_vcop_sobelY_3x3_uchar_char = vcop_heap_alloc(&heapInfo,
                                                          VCOP_WMEM,
                                                          64);
    __pblock_vcop_sobel_magnitude = vcop_heap_alloc(&heapInfo,
                                                    VCOP_WMEM,
                                                    32);

    /* ----------------------------------------------------------------- */
    /* Check to see if block width is a multiple of 8 since VCOP will    */
    /* process 8 pixels in SIMD.                                         */
    /* ----------------------------------------------------------------- */

    assert((BLK_WIDTH % 8) == 0);
    gAlg_edgeDetectProfile[0] = 0xDEADBEEF;

    /* ------------------------------------------------------------------ */
    /* Check to see if block width is a multiple of the image width and   */
    /* height.                                                            */
    /* ------------------------------------------------------------------ */

    assert((IMG_WIDTH % BLK_WIDTH) == 0);
    assert((IMG_HEIGHT % BLK_HEIGHT) == 0);

    /* ------------------------------------------------------------------ */
    /* Set memory view to aliased view, and change all buffers to SYSTEM. */
    /* ------------------------------------------------------------------ */

    VCOP_SET_MEM_VIEW (ALIAS_128K_VIEW);
    VCOP_BUF_SWITCH_SET (WBUF_SYST, IBUFHB_SYST, IBUFHA_SYST, IBUFLB_SYST, IBUFLA_SYST);


    /* ------------------------------------------------------------------ */
    /*  Anchor edma_cc to the start of the EDMA CC0                       */
    /* ------------------------------------------------------------------ */

    edma_cc  =  (CSL_EdmaccRegsOvly) (CSL_EDMACC_0_REGS);
    _tsc_start();


    /* ------------------------------------------------------------------ */
    /*  Set transfer status to 0 as no previous transfer has been fired.  */
    /* ------------------------------------------------------------------ */

    trf_status =  FALSE;


    /*--------------------------------------------------------------------*/
    /*  Declare a temporary variable to maintain state of status          */
    /*  transfer pending and pass the address to populate the             */
    /*  structure.                                                        */
    /*--------------------------------------------------------------------*/

    trf_pend       =      &trf_status;


    /* ------------------------------------------------------------------ */
    /*  Clear destination and processing buffers                          */
    /* ------------------------------------------------------------------ */

    // dst_buffers_setup();


    /* ------------------------------------------------------------------ */
    /* Configure channel for EDMA transfer. We will use two channels, one */
    /* for the input transfer and one for the output transfer.            */
    /* ------------------------------------------------------------------ */

    dma_attr       =       EDMA_CHAN;
    num_channels   =       NUM_CHANNELS;


    /* ------------------------------------------------------------------ */
    /* Assign unique events, queues and transfer complete codes to each   */
    /* channel.                                                           */
    /* ------------------------------------------------------------------ */

    ed_dma_assign_channels
    (
        evt_no_mem,
        tcc_no_mem,
        que_no_mem,
        reg_no_mem,
        dma_attr,
        num_channels
    );


    /*-------------------------------------------------------------------*/
    /*  Now populate the structure "dma_state_struct" based on the       */
    /*  resources allocated by the framework. This function also         */
    /*  captures the state of the global registers we are about to       */
    /*  re-program.                                                      */
    /*-------------------------------------------------------------------*/

    /* ------------------------------------------------------------------*/
    /* Number of PaRAMs per channel                                      */
    /* ------------------------------------------------------------------*/

    num_param = 1;

    ed_dma_state_struct_populate
    (
        edma_cc,
        &dma_state_struct,
        evt_no_mem,
        tcc_no_mem,
        reg_no_mem,
        que_no_mem,
        num_param,
        num_channels,
        dma_attr,
        trf_pend
    );



    /*-------------------------------------------------------------------*/
    /*  Re-program global registers to accomplish the transfer           */
    /*  settings user has asked for.                                     */
    /*-------------------------------------------------------------------*/

    ed_dma_global_regs_setup (edma_cc, &dma_state_struct);


    /* ----------------------------------------------------------------- */
    /* Set options field for input and output transfers                  */
    /* ----------------------------------------------------------------- */

    itccEn     =  CSL_EDMACC_OPT_ITCCHEN_DISABLE;
    tccEn      =  CSL_EDMACC_OPT_TCCHEN_DISABLE;
    itcintEn   =  CSL_EDMACC_OPT_ITCINTEN_ENABLE;
    tcintEn    =  CSL_EDMACC_OPT_TCINTEN_ENABLE;
    tccMode    =  CSL_EDMACC_OPT_TCCMODE_NORMAL;
    fwid       =  CSL_EDMACC_OPT_FWID_8;
    stat       =  CSL_EDMACC_OPT_STATIC_NORMAL;
    syncDim    =  CSL_EDMACC_OPT_SYNCDIM_ABSYNC;
    dam        =  CSL_EDMACC_OPT_DAM_INCR;
    sam        =  CSL_EDMACC_OPT_SAM_INCR;


    /* ----------------------------------------------------------------- */
    /* Options field calculation for 2D src to 1D dst.                   */
    /* Channel assignment to input ping/pong buffers.                    */
    /* These will be written to PARAM.                                   */
    /* ----------------------------------------------------------------- */

    chan_in   =  0x0;
    tcc_in    =  dma_state_struct.tcc_val[chan_in];

    opt_2d_1d =
    CSL_EDMA_OPT_MAKE
    (itccEn,tccEn,itcintEn,tcintEn,tcc_in,tccMode,fwid,stat,syncDim,dam,sam);


    /* ------------------------------------------------------------- */
    /* Options field calculation for 1D src to 2D dst.               */
    /* Channel assignment to output ping/pong buffers.               */
    /* These will be written to PARAM.                               */
    /* ------------------------------------------------------------- */

    chan_out   =  0x1;
    tcc_out    =  dma_state_struct.tcc_val[chan_out];

    opt_1d_2d  =
    CSL_EDMA_OPT_MAKE
    (itccEn,tccEn,itcintEn,tcintEn,tcc_out,tccMode,fwid,stat,syncDim,dam,sam);


    /* ------------------------------------------------------------- */
    /* Declare the pointers to the source and destination arrays in  */
    /* external memory.                                              */
    /* ------------------------------------------------------------- */

    src = (unsigned char *) inPtr[0];    //  + src_pitch;
    dst = (unsigned char *) outPtr[0];


    /* ------------------------------------------------------------- */
    /* Functions to write the DMA transfer attributes to PARAM       */
    /* src_proc_ping/dst_proc_ping are passed as parameters. The     */
    /* pong buffers are not passed as arguments since this example   */
    /* is in aliased memory view. VCOP/EDMA hardware knows which     */
    /* buffer (ping/pong) to access based on buffer ownership as     */
    /* defined by the macros VCOP_BUF_SWITCH_SET() and               */
    /* VCOP_BUF_SWITCH_TOGGLE().                                     */
    /* ------------------------------------------------------------- */

    /* ------------------------------------------------------------- */
    /* 2D to 1D, EXT to INT Transfers                                */
    /* ------------------------------------------------------------- */

    ed_params_2d_to_1d_eve
    (
        &dma_state_struct,
        chan_in,
        opt_2d_1d,
        src,                     // SRC (external mem)
        src_proc_ping,           // DST (internal mem)
        num_bytes + pad,         // bytes per line in block + 2 PAD
        num_lines + pad,         // # of lines in block + 2 PAD
        src_pitch,               // line pitch of input image
        num_bytes,               // offset to next block
        (IMG_WIDTH/BLK_WIDTH)+1
    );


    /* ------------------------------------------------------------- */
    /* 1D to 2D, INT to EXT Transfers                                */
    /* ------------------------------------------------------------- */

    ed_params_1d_to_2d_eve
    (
        &dma_state_struct,
        chan_out,
        opt_1d_2d,
        dst_proc_ping,          // SRC (internal mem)
        dst,                     // DST (external mem)
        num_bytes/2,               // bytes per line in block
        num_lines/2,               // # of lines in block
        dst_pitch/2,               // line pitch of output image is 768
        num_bytes/2 - 2,               // offset to next block      
        (IMG_WIDTH/BLK_WIDTH)+1
    );


    /* ------------------------------------------------------------- */
    /* Call algo_dma_auto_incr:                                      */
    /* This function implements dma double buffering with auto       */
    /* incrementing. Transfers to/from external memory are           */
    /* triggered. Within this subroutine a VCOP assembly function    */
    /* (vcop_copy_data_per_8) is called that will copy the source    */
    /* block to the destination block before it is transferred to    */
    /* external memory.                                              */
    /* ------------------------------------------------------------- */

    t0 = _tsc_gettime();
    algo_dma_auto_incr
    (
        edma_cc,
        &dma_state_struct,
        (unsigned char *) inPtr[0],
        (unsigned char *) outPtr[0],
        src_proc_ping,
        dst_proc_ping,
        OUTX,
        OUTY,
        inter_dat,
        chan_in,
        chan_out,
        trf_pend,
        inPitch
     );
    t1 = _tsc_gettime();
    gAlg_edgeDetectProfile[0] = t1 - t0;

    VCOP_BUF_SWITCH_SET (WBUF_SYST, IBUFHB_SYST, IBUFHA_SYST, IBUFLB_SYST, IBUFLA_SYST);

    /* ------------------------------------------------------------- */
    /* Clear the event registers                                     */
    /* ------------------------------------------------------------- */

    ed_dma_global_regs_teardown (edma_cc, &dma_state_struct);

    gAlg_edgeDetectProfile[1] = 0xDEADBEEF;
    /* ------------------------------------------------------------- */
    /* Check to make sure there are no more pending transfers        */
    /* ------------------------------------------------------------- */

    assert(edma_cc->IPR == 0);


    // End of Edge Detection Function

    return SYSTEM_LINK_STATUS_SOK;
}

/**
 *******************************************************************************
 *
 * \brief Implementation of Control for frame copy algo
 *
 * \param  algHandle             [IN] Algorithm object handle
 * \param  pControlParams        [IN] Pointer to Control Params
 *
 * \return  SYSTEM_LINK_STATUS_SOK on success
 *
 *******************************************************************************
 */
Int32 Alg_EdgeDetectionControl(Alg_EdgeDetection_Obj          *pAlgHandle,
                           Alg_EdgeDetectionControlParams *pControlParams)
{
    /*
     * Any alteration of algorithm behavior
     */
    return SYSTEM_LINK_STATUS_SOK;
}

/**
 *******************************************************************************
 *
 * \brief Implementation of Stop for frame copy algo
 *
 * \param  algHandle    [IN] Algorithm object handle
 *
 * \return  SYSTEM_LINK_STATUS_SOK on success
 *
 *******************************************************************************
 */
Int32 Alg_EdgeDetectionStop(Alg_EdgeDetection_Obj *algHandle)
{
      return SYSTEM_LINK_STATUS_SOK;
}

/**
 *******************************************************************************
 *
 * \brief Implementation of Delete for frame copy algo
 *
 * \param  algHandle    [IN] Algorithm object handle
 *
 * \return  SYSTEM_LINK_STATUS_SOK on success
 *
 *******************************************************************************
 */
Int32 Alg_EdgeDetectionDelete(Alg_EdgeDetection_Obj *algHandle)
{
    free(algHandle);
    return SYSTEM_LINK_STATUS_SOK;
}

/* Nothing beyond this point */
