/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support
 * ----------------------------------------------------------------------------
 * Copyright (c) 2009, Atmel Corporation
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Atmel's name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "image_capture.h"

/*----------------------------------------------------------------------------
 *        Local definitions
 *----------------------------------------------------------------------------*/

#define IMAGE_WIDTH     (320)
#define IMAGE_HEIGHT    (240)
/** the captured data length per line in different color mode,double the size
in YUV full output*/
#define IMAGE_LINE_COLORED    (IMAGE_WIDTH * 2)
#define IMAGE_LINE_MONO  (IMAGE_WIDTH)

#define PIO_PCMR_DSIZE_8 (0x0<<4)
#define PIO_PCMR_DSIZE_16 (0x1<<4)
#define PIO_PCMR_DSIZE_32 (0x2<<4)

/** TWI clock frequency in Hz (400KHz) */
#define TWCK            400000

#define ON  true
#define OFF false

/** The output formats supported by the sensor*/
typedef enum {
    OUT_RGB,
    OUT_YUV
}OUT_FMT;

/** the output color modes supported by the sensor*/
typedef enum{
    OUT_MONO,
    OUT_COLOR
}OUT_MODE;

#define DEFAULT_SENSOR_YUV422
/** uncomment for monochrome output*/
#define DEFAULT_MODE_COLORED

#define SRAM_BASE (0x60000000)
#define CAP_DEST  (SRAM_BASE)

/*----------------------------------------------------------------------------
 *        Local variables
 *----------------------------------------------------------------------------*/
/** the output color mode of image sensor*/
static OUT_MODE out_mode = OUT_MONO;

/** capturing destination buffer*/
static uint8_t *cap_dest_buf;

/** cpaturing rows*/
static uint16_t cap_rows = IMAGE_HEIGHT;

/** cap line length*/
static uint16_t cap_line = IMAGE_LINE_COLORED;

/** VSYNC flag*/
static volatile bool vsync_flag = false;

/** TWI driver*/
static Twid twid;

static const Pin pinVSYNC = PIN_OV_VSYNC;

/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/
/** status flag for tranfer*/
static bool _PIO_Capture_BUFF(Pio *pio)
{
    return ((pio->PIO_PCISR & PIO_PCIMR_RXBUFF) == PIO_PCIMR_RXBUFF)?true:false;
}

/** PIO capture with PDC*/
static uint8_t _PIO_CaptureToBuffer(Pio *pio, uint8_t *buf, uint32_t size)
{
    /* check if the first PDC bank is free*/
    if( ( pio->PIO_RCR == 0) && (pio->PIO_RNCR == 0)){

        pio->PIO_RPR = (uint32_t)buf;
        pio->PIO_RCR = size;
        pio->PIO_PTCR = PIO_PTCR_RXTEN;

        return 1;
    }else if(pio->PIO_RNCR == 0){

        pio->PIO_RNPR = (uint32_t)buf;
        pio->PIO_RNCR = size;
        return 1;
    }else{
        return 0;
    }
}

/** switch pio capturing on or off*/
static void _PIO_Capture_Switch(Pio *pio,bool on_off)
{
    if(on_off)
    {
        pio->PIO_PCMR |= PIO_PCMR_PCEN;
    }
    else
    {
        pio->PIO_PCMR &= (~(uint32_t)PIO_PCMR_PCEN);
    }
}

/** power control for image sensor*/
static void _Turn_ImageSensor(bool on)
{
    const Pin pinsPower[]={ PINS_OV };
    PIO_Configure(pinsPower,PIO_LISTSIZE(pinsPower));
    if(on){
        PIO_Clear(&pinsPower[0]);

    }else
    {
        PIO_Set(&pinsPower[0]);
    }
}

/** image sensor initialization, ov7740*/
static void _Init_Image_Sensor(const EOV7740_Format eFormat)
{
    /* intitializ Master Clock to xvclk1*/
    Pin pinPCK = PIN_PCK0;
    const Pin pinsTWI[] = {PINS_TWI0};
    Pmc *pmc = (Pmc*) PMC;

    /* Power on*/
    _Turn_ImageSensor(true);

    PIO_Configure(&pinPCK,1);

    /* PLLA is 96MHz so that PCK0 is 96MHz/4 = 24MHz*/
    pmc->PMC_PCK[0] = (PMC_PCK_PRES_DIV4 | PMC_PCK_CSS_PLLACLOCK);

    pmc->PMC_SCER = PMC_SCER_PCK0;

    while(!( pmc->PMC_SCSR & PMC_SCSR_PCK0));
    Wait(5);

    /* twi*/
    PIO_Configure(pinsTWI,PIO_LISTSIZE(pinsTWI));

    PMC_EnablePeripheral(ID_TWI0);

    TWI_ConfigureMaster(TWI0, TWCK, BOARD_MCK);

    TWID_Initialize(&twid, TWI0);	

    /* Configure TWI interrupts */
    NVIC_DisableIRQ(TWI0_IRQn);
    NVIC_ClearPendingIRQ(TWI0_IRQn);
    NVIC_SetPriority(TWI0_IRQn, 0);
    NVIC_EnableIRQ(TWI0_IRQn);

    while( ov_init(&twid) == 0 ) {
	printf("-I- Retry init\n\r");
    }

    printf("-I- Init passed\n\r");
    /* OV7740 configuration */
    ov_configure(&twid, eFormat);
}


/** VSync Hanlder*/
static void _Vsync_Handler(const Pin *pin)
{
    vsync_flag = true;
}

/** VSync handler intialization*/
static void _Init_HVSync_Interrupts(void)
{
    PIO_Configure(&pinVSYNC,1);

    PIO_ConfigureIt(&pinVSYNC, _Vsync_Handler);

    /* Enable PIO controller IRQs. */
    NVIC_EnableIRQ(PIOA_IRQn);
}

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/** initialization for capturing */
extern void Capture_Init( void )
{
    /* OUTPUT color mode*/
#ifdef DEFAULT_MODE_COLORED
    out_mode = OUT_COLOR;
#else
    out_mode = OUT_MONO;
#endif
    /* configure SRAM*/
    BOARD_ConfigureSRAM(SMC) ;

    /* intialize Frame signal*/
    _Init_HVSync_Interrupts();

    /* initialize PIODC*/
    PIODC_Capture_Init(PIOA, ID_PIOA, out_mode);

    /** intialize capturing line*/
    if(out_mode == OUT_COLOR){
        cap_line = IMAGE_LINE_COLORED;
    }else if(out_mode == OUT_MONO){
        cap_line = IMAGE_LINE_MONO;
    }else{
        cap_line = IMAGE_LINE_COLORED;
    }
#ifdef DEFAULT_SENSOR_YUV422
    _Init_Image_Sensor(QVGA_YUV422);
#endif

}

/** interface for doing capture*/
void DoCapture(uint8_t *dest)
{
    uint8_t *buf;

    /* set capturing destination address*/
    cap_dest_buf = dest;

    buf = cap_dest_buf;

    cap_rows = IMAGE_HEIGHT;

    /* enable vsync interrupt*/
    PIO_EnableIt(&pinVSYNC);

    /* sync with flag*/
    while(!vsync_flag);

    /* disable first*/
    PIO_DisableIt(&pinVSYNC);

    _PIO_Capture_Switch(PIOA,ON);

    if(vsync_flag){
        /* only using Vsync*/
        _PIO_CaptureToBuffer(PIOA,buf, (cap_line * cap_rows)>>2);
        while(!_PIO_Capture_BUFF(PIOA));

       _PIO_Capture_Switch(PIOA,OFF);
        vsync_flag = false;
    }
}
