/*******************************************************************************
 * (c) Copyright 2016-2017 Microsemi SoC Products Group. All rights reserved.
 * 
 * This SoftConsole Video project for MIPI sensor configuration and interfacing with GUI
 *
 * Please refer README.TXT in the root folder of this project for more details.
 */
#include "miv_rv32_hal/miv_rv32_hal.h"
#include "drivers/fpga_ip/CoreGPIO/core_gpio.h"
#include "drivers/fpga_ip/CoreUARTAPB/core_uart_apb.h"
#include "drivers/fpga_ip/CoreI2C/core_i2c.h"
#include "drivers/fpga_ip/CoreAXI4-Lite/AXI4-Lite.h"
#include "imx334_corei2c/imx334_corei2c.h"
#include "dp_cmd_common.h"
#include "dp_cmd_tx.h"
#include "dp_cmd_common.h"
#include "dp_cmd_tx_regs.h"
#include "dp_cmd_tx.h"

/*-----------------------------------------------------------------------------
                          Address Definition
 -----------------------------------------------------------------------------*/

#define LED1 GPIO_0
#define LED2 GPIO_1
#define LED3 GPIO_2
#define LED4 GPIO_3

#define MIPI_TRNG_RST GPIO_4

// Bayer Configuration
#define BAYER_ADDR                0x72020000

// Display Controller Configuration
#define DC_IP_VER                 0x72040000
#define DC_IP_EN_DIS              0x72040004
#define DC_IP_HRES                0x72040008
#define DC_IP_VRES                0x7204000C
#define DC_IP_HFP                 0x72040010
#define DC_IP_HBP                 0x72040014
#define DC_IP_VFP                 0x72040018
#define DC_IP_VBP                 0x7204001C
#define DC_IP_HSW                 0x72040020
#define DC_IP_VSW                 0x72040024

// Image Enhancment Configuration
#define IE_IP_VER                 0x72030000
#define IE_IP_EN_DIS              0x72030004
#define IE_R_CONST                0x72030008
#define IE_G_CONST                0x7203000C
#define IE_B_CONST                0x72030010
#define IE_COMMON_CONST           0x72030014

///
#define IE_INTENSITY_AVARAGE      0x71002038

static uint16_t in_gain = 80;
static void auto_brightness( uint32_t div);
static void gain_cal(uint32_t total_average);

gpio_instance_t g_gpio_out;
i2c_instance_t g_i2c_instance_cam1;
UART_instance_t g_uart;

/*-----------------------------------------------------------------------------
                          Global state counter.
 -----------------------------------------------------------------------------*/
uint32_t g_state = 1;
uint32_t t_ms_count = 0;
uint32_t R_constant=146;
uint32_t G_constant=122;
uint32_t B_constant=165;
uint32_t second_constant=0;
uint8_t uart_rx_buff[23]={0};
uint32_t Enable=1;
uint32_t Disable=0;
uint32_t div = (1920*1080*3);



volatile uint32_t g_10ms_count;
volatile uint32_t timerdone = 0;
volatile uint32_t g_10ms_count1;
volatile uint32_t g_ms_count;
volatile uint32_t rx_tmr_done = 0;
volatile uint32_t rx_ms_count1;
volatile uint32_t rx_ms_count;

/*-----------------------------------------------------------------------------
 * UART handler specific.
 -----------------------------------------------------------------------------*/
uint32_t i = 0;
uint32_t process_data = 0;

/*-----------------------------------------------------------------------------
 * System Tick interrupt handler
----------------------------------------------------------------------------- */
void SysTick_Handler(void) {

    g_state = (~g_state) & 0x01;

    if(timerdone == 1)
    {
        g_10ms_count1 += 1;
        if(g_ms_count <= g_10ms_count1)
            timerdone = 0;
    }

    if(rx_tmr_done == 1)
    {
        rx_ms_count1 += 1;
        if(rx_ms_count1 >= rx_ms_count){
            rx_tmr_done = 0;
            process_data = 1;
        }
    }
}

uint8_t  MSYS_EI0_IRQHandler(void)
{
    I2C_isr(&g_i2c_instance_cam1);
    return (EXT_IRQ_KEEP_ENABLED);
}

/*-----------------------------------------------------------------------------
 * main
----------------------------------------------------------------------------- */
uint32_t a;

int main(int argc, char **argv) {
    volatile  uint32_t counter;
    uint8_t state;
    counter = 0;
    state = 0;

    // ---- Parameters for Display Port in FHD Mode
#if 0
    uint32_t PIXEL_MODE=1;//4 PIXEL MODE HRES DEIVIDE BY 4
    uint32_t BAYER_OFFSET_VALUE=2;
    uint32_t SPEED_MODE=1;
    uint32_t HRES=1920;
    uint32_t VRES=1080;
    uint32_t HFP=88;
    uint32_t HSW=44;
    uint32_t HBP=148;
    uint32_t VFP=4;
    uint32_t VSW=5;
    uint32_t VBP=36;
    uint32_t VSP=0x00000000;
    uint32_t LANE_NO=0x00000004;
#endif
    // ---- Parameters for Display Port in 4K resolution
#if 1
    uint32_t PIXEL_MODE=4;//4 PIXEL MODE HRES DEIVIDE BY 4
    uint32_t BAYER_OFFSET_VALUE=0;
    uint32_t SPEED_MODE=2;
    uint32_t HRES=3840;
    uint32_t VRES=2160;
    uint32_t HFP=76;
    uint32_t HSW=8;
    uint32_t HBP=76;
    uint32_t VFP=54;
    uint32_t VSW=5;
    uint32_t VBP=3;
    uint32_t VSP=0x00008000;
    uint32_t LANE_NO=0x00000004;
#endif

    Enable = 0x01;
    Disable = 0x00;

    // Bayer Configuration
    axi4litewrite(BAYER_ADDR,BAYER_OFFSET_VALUE);

    // Display Controller Configuration
    axi4litewrite(DC_IP_EN_DIS,Disable);

    axi4litewrite(DC_IP_HRES,HRES/PIXEL_MODE);
    axi4litewrite(DC_IP_VRES,VRES);
    axi4litewrite(DC_IP_HFP,HFP/PIXEL_MODE);
    axi4litewrite(DC_IP_HBP,HBP/PIXEL_MODE);
    axi4litewrite(DC_IP_VFP,VFP);
    axi4litewrite(DC_IP_VBP,VBP);
    axi4litewrite(DC_IP_HSW,HSW/PIXEL_MODE);
    axi4litewrite(DC_IP_VSW,VSW);

    axi4litewrite(DC_IP_EN_DIS,Enable);


    // Image Enhancmnet Configuration
    axi4litewrite(IE_IP_EN_DIS,Disable);

    axi4litewrite(IE_R_CONST,R_constant);
    axi4litewrite(IE_G_CONST,G_constant);
    axi4litewrite(IE_B_CONST,B_constant);
    axi4litewrite(IE_COMMON_CONST,second_constant);


    axi4litewrite(IE_IP_EN_DIS,Enable);


    GPIO_init(&g_gpio_out, COREGPIO_OUT_BASE_ADDR, GPIO_APB_32_BITS_BUS);
    GPIO_set_output(&g_gpio_out, LED1, 1);

    MRV_systick_config(SYS_CLK_FREQ / 1000);

    MRV_enable_local_irq(MRV32_MSYS_EIE0_IRQn);

    HAL_enable_interrupts();

    GPIO_set_output(&g_gpio_out, MIPI_TRNG_RST, 0u);
    GPIO_set_output(&g_gpio_out, LED2, 1);

    //Camera Initialization
    GPIO_set_output(&g_gpio_out, CAM1_RST, 1u);
    GPIO_set_output(&g_gpio_out, CAM_CLK_EN, 0u);
    imx334_cam_init();
    imx334_cam_reginit(1u);

    //Setting LED
    GPIO_set_output(&g_gpio_out, LED3, 1);
    msdelay(1000);
    GPIO_set_output(&g_gpio_out, MIPI_TRNG_RST, 1u);
    GPIO_set_output(&g_gpio_out, LED4, 1);

    // =========================== DisplayPort ================================
    UART_init(&g_uart, COREUARTAPB0_BASE_ADDR, BAUD_VALUE_115200, (DATA_8_BITS | NO_PARITY));
    DPSourceInit(SPEED_MODE,HRES,VRES,HFP,HBP,VFP,VBP,HSW,VSW,VSP,LANE_NO);

        // ====================================================================
        // ===================== DisplayPort ==================================
        // ====================================================================
do
    {
        DPSourceISR(SPEED_MODE,HRES,VRES,HFP,HBP,VFP,VBP,HSW,VSW,VSP,LANE_NO);
        msdelay(30);
        auto_brightness(div);
        counter = counter +1;
        if (counter <16){
            GPIO_set_output(&g_gpio_out, LED1, 0);
        }
        else{
            GPIO_set_output(&g_gpio_out, LED1, 1);
        }
        if (counter ==32){
            counter =0;
        }
    } while (1);
    return 0;
}

/**********************************************************/
/**********************FUNCTION CALLS**********************/
/**********************************************************/
void auto_brightness(uint32_t div)
    {
        uint32_t total_sum =  (uint32_t)(*(volatile int*) IE_INTENSITY_AVARAGE);
        uint32_t total_average = total_sum/div;

        gain_cal(total_average);
    }

void gain_cal(uint32_t total_average)
    {
        const int16_t good_average=100;
        const int16_t hysteresis=4;
        int16_t step;
            if(total_average < (good_average - hysteresis))
                step = 1;
            else
                if(total_average > (good_average + hysteresis))
                    step = -1;
                else
                    step = 0;

            in_gain = in_gain + step;

            if(in_gain < 5)
                in_gain = 5;
            else
                if(in_gain >= 100)
                    in_gain = 100;

        gain_setting(1u,in_gain);
    }
