/* ----------------------------------------------------------------------------
 *         SAM Software Package License 
 * ----------------------------------------------------------------------------
 * Copyright (c) 2013, 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 "board.h"

/** Slave address of OMNIVISION chips. */
#define OV_CAPTOR_ADDRESS_1   0x30 
#define OV_CAPTOR_ADDRESS_2   0x21
#define OV_CAPTOR_ADDRESS_3   0x3c 
#define OV_CAPTOR_ADDRESS_4   0x10

/** terminating list entry for register in configuration file */
#define OV_REG_TERM 0xFF
#define OV_REG_DELAY 0xFFFF
/** terminating list entry for value in configuration file */
#define OV_VAL_TERM 0xFF 

static const Pin pin_ISI_RST= BOARD_ISI_RST;
static uint8_t twiSlaveAddr = OV_CAPTOR_ADDRESS_1;
/*----------------------------------------------------------------------------
 *        Local Functions
 *----------------------------------------------------------------------------*/
static void ov_reset(void)
{
	volatile uint32_t i;
	PIO_Configure(&pin_ISI_RST, 1);
	PIO_Clear(&pin_ISI_RST);
	for(i = 0; i < 6000; i++ );
	PIO_Set(&pin_ISI_RST);
	for(i = 0; i<6000; i++ );
}


/**
 * \brief  Read PID and VER
 * \param pTwid TWI interface
 * \return  VER | (PID<<8)
 */
static uint16_t ov_id8(Twid *pTwid)
{
	uint8_t id, ver;
	uint8_t status;
	// OV_PID
	status = ov_read_reg8(pTwid, 0x0A, &id);
	if( status != 0 )
		return 0;
	TRACE_INFO("PID  = 0x%X\n\r", id);

	// OV_VER
	status = ov_read_reg8(pTwid, 0x0B, &ver);
	if( status != 0 )
		return 0;
	TRACE_INFO("VER  = 0x%X\n\r", ver);

	return((uint16_t)(id <<8) | ver);
}

/**
 * \brief  Read PID and VER
 * \param pTwid TWI interface
 * \return  VER | (PID<<8)
 */
static uint16_t ov_id16(Twid *pTwid)
{
	uint8_t id, ver;
	// OV_PID
	ov_read_reg16(pTwid, 0x300A, &id);
	TRACE_INFO("PID  = 0x%X\n\r", id);

	// OV_VER
	ov_read_reg16(pTwid, 0x300B, &ver);
	TRACE_INFO("VER  = 0x%X\n\r", ver);

	return((uint16_t)(id <<8) | ver);
}

/**
 * \brief  Read PID and VER
 * \param pTwid TWI interface
 * \return  VER | (PID<<8)
 */
static uint16_t ov_id(Twid *pTwid)
{
	uint16_t id;
	TRACE_INFO(" Try TWI address 0x%x \n\r", twiSlaveAddr);
	twiSlaveAddr = OV_CAPTOR_ADDRESS_1;
	id = ov_id8(pTwid);
	if (id == 0) {
		twiSlaveAddr = OV_CAPTOR_ADDRESS_2;
		TRACE_INFO("Try TWI address 0x%x \n\r", twiSlaveAddr);
		id = ov_id8(pTwid);
		if (id == 0) {
			twiSlaveAddr = OV_CAPTOR_ADDRESS_3;
			TRACE_INFO("Try TWI address 0x%x \n\r", twiSlaveAddr);
			id = ov_id16(pTwid);
			if (id == 0) {
				twiSlaveAddr = OV_CAPTOR_ADDRESS_4;
				TRACE_INFO("Try TWI address 0x%x \n\r", twiSlaveAddr);
				id = ov_id16(pTwid);
			}
		}
	}
	return id;
}


/*----------------------------------------------------------------------------
 *        Global Functions
 *----------------------------------------------------------------------------*/
/**
 * \brief  Read a value from a register in an OV sensor device.
 * \param pTwid TWI interface
 * \param reg Register to be read
 * \param pData Data read
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint8_t ov_read_reg8(Twid *pTwid, uint8_t reg, uint8_t *pData)
{
	uint8_t status;

	status = TWID_Write( pTwid, twiSlaveAddr, 0, 0, &reg, 1, 0);
	status |= TWID_Read( pTwid, twiSlaveAddr, 0, 0, pData, 1, 0);
	if( status != 0 ) {
		TRACE_ERROR("ov_read_reg pb\n\r");
	}
	return status;
}

/**
 * \brief  Read a value from a register in an OV sensor device.
 * \param pTwid TWI interface
 * \param reg Register to be read
 * \param pData Data read
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint8_t ov_read_reg16(Twid *pTwid, uint16_t reg, uint8_t *pData)
{
	uint8_t status;
	uint8_t reg8[2];
	reg8[0] = reg>>8;
	reg8[1] = reg & 0xff;

	status = TWID_Write( pTwid, twiSlaveAddr, 0, 0, reg8,  2, 0);
	status |= TWID_Read( pTwid, twiSlaveAddr, 0, 0, pData, 1, 0);
	if( status != 0 ) {
		TRACE_ERROR("ov_read_reg pb\n\r");
	}
	return status;
}

/**
 * \brief  Write a value to a register in an OV sensor device.
 * \param pTwid TWI interface
 * \param reg Register to be written
 * \param pData Data written
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint8_t ov_write_reg8(Twid *pTwid, uint8_t reg, uint8_t val)
{
	uint8_t status;

	status = TWID_Write(pTwid, twiSlaveAddr, reg, 1, &val, 1, 0);
	if( status != 0 ) {
		TRACE_ERROR("ov_write_reg pb\n\r");
	}
	return status;
}

/**
 * \brief  Write a value to a register in an OV sensor device.
 * \param pTwid TWI interface
 * \param reg Register to be written
 * \param pData Data written
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint8_t ov_write_reg16(Twid *pTwid, uint16_t reg,  uint8_t val)
{
	uint8_t status;
	status = TWID_Write(pTwid, twiSlaveAddr, reg, 2, &val, 1, 0);
	if( status != 0 ) {
		TRACE_ERROR("ov_write_reg pb\n\r");
	}

	return status;
}


/**
 * \brief  Initialize a list of OV registers.
 * The list of registers is terminated by the pair of values
 * \param pTwid TWI interface
 * \param pReglist Register list to be written
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint32_t ov_write_regs8(Twid *pTwid, const struct ov_reg* pReglist)
{
	uint32_t err;
	uint32_t size=0;
	const struct ov_reg *pNext = pReglist;
	volatile uint32_t delay;

	TRACE_DEBUG("ov_write_regs:");
	while (!((pNext->reg == OV_REG_TERM) && (pNext->val == OV_VAL_TERM))) {
		err = ov_write_reg8(pTwid, pNext->reg, pNext->val);
		
		size++;
		for(delay=0;delay<=10000;delay++); 
		if (err == TWID_ERROR_BUSY){
			TRACE_ERROR("ov_write_regs: TWI ERROR\n\r");
			return err;
		}
		pNext++;
	}
	TRACE_DEBUG_WP("\n\r");
	return 0;
}


/**
 * \brief  Initialize a list of OV registers.
 * The list of registers is terminated by the pair of values
 * \param pTwid TWI interface
 * \param pReglist Register list to be written
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint32_t ov_write_regs16(Twid *pTwid, const struct ov_reg* pReglist)
{
	uint32_t err = 0;
	uint32_t size = 0;
	const struct ov_reg *pNext = pReglist;
	volatile uint32_t delay;

	TRACE_DEBUG("ov_write_regs:");
	while (!((pNext->reg == OV_REG_TERM) && (pNext->val == OV_VAL_TERM))) {
		err = ov_write_reg16(pTwid, pNext->reg, pNext->val);
		size++;
		for(delay = 0;delay <= 10000; delay++); 
		if (err == TWID_ERROR_BUSY){
			TRACE_ERROR("ov_write_regs: TWI ERROR\n\r");
			return err;
		}
		pNext++;
	}
	TRACE_DEBUG_WP("\n\r");
	return 0;
}

void isOV5640_AF_InitDone(Twid *pTwid)
{
	uint8_t value = 0;
	while(1){
		ov_read_reg16(pTwid, 0x3029, &value);
		if (value == 0x70)
			break;
	}
}

/**
 * \brief  AF for OV 5640
 * \param pTwid TWI interface
 * \return 0 if no error; otherwise TWID_ERROR_BUSY
 */
uint32_t ov_5640_AF_single(Twid *pTwid)
{
	uint8_t value;
	ov_write_reg16(pTwid, 0x3023, 1);
	ov_write_reg16(pTwid, 0x3022, 3);
	value =1;
	while(1){
		ov_read_reg16(pTwid, 0x3023, &value);
		if (value == 0) 
			break;
	}
	return 0;
}

uint32_t ov_5640_AF_continue(Twid *pTwid)
{
	uint8_t value;
	ov_write_reg16(pTwid, 0x3024, 1);
	ov_write_reg16(pTwid, 0x3022, 4);
	value =1;
	while(1){
		ov_read_reg16(pTwid, 0x3023, &value);
		if (value == 0) 
			break;
	}
	return 0;
}

uint32_t ov_5640_AFPause(Twid *pTwid)
{
	uint8_t value;
	ov_write_reg16(pTwid, 0x3023, 1);
	ov_write_reg16(pTwid, 0x3022, 6);
	value =1;
	while(1){
		ov_read_reg16(pTwid, 0x3023, &value);
		if (value == 0)
			break;
	}
	return 0;
}

uint32_t ov_5640_AFrelease(Twid *pTwid)
{
	uint8_t value;
	ov_write_reg16(pTwid, 0x3023, 1);
	ov_write_reg16(pTwid, 0x3022, 8);
	value =1;
	while(1){
		ov_read_reg16(pTwid, 0x3023, &value);
		if (value == 0)
			break;
	}
	return 0;
}

/**
 * \brief  Dump all register
 * \param pTwid TWI interface
 */
void ov_DumpRegisters8(Twid *pTwid)
{
	uint32_t i;
	uint8_t value;

	TRACE_INFO_WP("Dump all camera register\n\r");
	for(i = 0; i <= 0x5C; i++) {
		value = 0;
		ov_read_reg8(pTwid, i,  &value);
		TRACE_INFO_WP("[0x%02x]=0x%02x ", i, value);
		if( ((i+1)%5) == 0 ) {
			TRACE_INFO_WP("\n\r");
		}
	}
	TRACE_INFO_WP("\n\r");
}

/**
 * \brief  Dump all register
 * \param pTwid TWI interface
 */
void ov_DumpRegisters16(Twid *pTwid)
{
	uint32_t i;
	uint8_t value;

	TRACE_INFO_WP("Dump all camera register\n\r");
	for(i = 3000; i <= 0x305C; i++) {
		value = 0;
		ov_read_reg16(pTwid, i, &value);
		TRACE_INFO_WP("[0x%02x]=0x%02x ", i, value);
		if( ((i+1)%5) == 0 ) {
			TRACE_INFO_WP("\n\r");
		}
	}
	TRACE_INFO_WP("\n\r");
}

/**
 * \brief Sequence For correct operation of the sensor
 * \param pTwid TWI interface
 * \return OV type
 */
uint8_t ov_init(Twid *pTwid)
{
	uint16_t id = 0;
	uint8_t ovType;
	ov_reset();
	id = ov_id(pTwid);
	switch (id) {
	case 0x7740: case 0x7742:
		ovType =  OV_7740;
		TRACE_INFO(" Camera Model :- OV_7740");
		break;
	case 0x9740: case 0x9742:
		ovType =  OV_9740;
		TRACE_INFO(" Camera Model :- OV_9740");
		break;
	case 0x2642: case 0x2640:
		ovType =  OV_2640;
		TRACE_INFO(" Camera Model :- OV_2640");
		break;
	case 0x2643: 
		ovType =  OV_2643;
		TRACE_INFO(" Camera Model :- OV_2643");
		break;
	case 0x5640:
		ovType =  OV_5640;
		TRACE_INFO(" Camera Model :- OV_5640");
		break;
	default:
		ovType = OV_UNKNOWN;
		TRACE_INFO(" Camera Model :- UNKNOWN");
		TRACE_ERROR("Can not support product ID %x \n\r", id);
		break;
	}
	return ovType;
}
