//*****************************************************************************
//
//! @file am_devices_t5838.c
//!
//! @brief TDK T5838 driver.
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_mcu_apollo.h"
#include "am_bsp.h"

#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

#include "am_devices_t5838.h"

#if configUSE_AAD

//*****************************************************************************
//
// Global variables.
//
//*****************************************************************************
#if defined (AM_PART_APOLLO3_API)
#define T5838_PDM_CLK           AM_BSP_GPIO_PDMCLK
#else
#define T5838_PDM_CLK           AM_BSP_GPIO_PDM0_CLK
#endif

#define T5838_PILOT_WIDTH_CLK   10
#define T5838_CLK_PERIOD_US     10
#define T5838_CLK_EDGE_US       (T5838_CLK_PERIOD_US / 2)

#define T5838_WRITE_ONE_CLK     (T5838_PILOT_WIDTH_CLK * 3)
#define T5838_WRITE_ZERO_CLK    T5838_PILOT_WIDTH_CLK

#define T5838_IDLE_PERIOD_CLK   55
//#define T5838_IDLE_PERIOD_CLK   160
#define T5838_START_PERIOD_CLK  T5838_PILOT_WIDTH_CLK
#define T5838_STOP_PERIOD_CLK   128
//#define T5838_STOP_PERIOD_CLK   160

#define T5838_REG_DUMP          0

void am_device_t5838_delay_us(uint32_t ui32Cycles)
{
#if defined (AM_PART_APOLLO3_API)
    am_util_delay_us(ui32Cycles);
#else
    am_hal_delay_us(ui32Cycles);
#endif
}

void am_device_t5838_clk_cycle(uint32_t ui32Cycles)
{
    am_device_t5838_delay_us(2);
    for(int i = 0; i < ui32Cycles - 1; i++)
    {
        am_hal_gpio_state_write(T5838_PDM_CLK, AM_HAL_GPIO_OUTPUT_SET);
        am_device_t5838_delay_us(T5838_CLK_EDGE_US);
        am_hal_gpio_state_write(T5838_PDM_CLK, AM_HAL_GPIO_OUTPUT_CLEAR);
        am_device_t5838_delay_us(T5838_CLK_EDGE_US);
    }
    am_hal_gpio_state_write(T5838_PDM_CLK, AM_HAL_GPIO_OUTPUT_SET);
    am_device_t5838_delay_us(T5838_CLK_EDGE_US);
    am_hal_gpio_state_write(T5838_PDM_CLK, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_device_t5838_delay_us(3);
}

void am_device_t5838_one_clk_cycle(void)
{
    am_device_t5838_delay_us(2);
    am_hal_gpio_state_write(T5838_PDM_CLK, AM_HAL_GPIO_OUTPUT_SET);
    am_device_t5838_delay_us(T5838_CLK_EDGE_US);
    am_hal_gpio_state_write(T5838_PDM_CLK, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_device_t5838_delay_us(3);
}

void am_device_t5838_start_cmd(void)
{
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_device_t5838_clk_cycle(T5838_IDLE_PERIOD_CLK);
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_SET);
    am_device_t5838_clk_cycle(T5838_START_PERIOD_CLK);
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_CLEAR);
}

void am_device_t5838_stop_cmd(void)
{
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_SET);
    am_device_t5838_clk_cycle(T5838_STOP_PERIOD_CLK);
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_device_t5838_clk_cycle(T5838_IDLE_PERIOD_CLK);
}

void am_device_t5838_space_cmd(void)
{
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_device_t5838_clk_cycle(T5838_PILOT_WIDTH_CLK);
}

int8_t am_device_t5838_read_bit(void)
{
    uint32_t ui32PinVal;
    uint8_t ui8LowCount = 0;
    uint8_t ui8Result = 0;
    for(int i = 0; i < T5838_PILOT_WIDTH_CLK * 3; i++)
    {
        am_device_t5838_one_clk_cycle();
        am_hal_gpio_state_read(AM_T5838_THSEL_PIN, AM_HAL_GPIO_INPUT_READ, &ui32PinVal);
        if(ui32PinVal == 0)
            ui8LowCount++;

        if(i == T5838_PILOT_WIDTH_CLK * 2)
        {
            if(ui8LowCount >= (T5838_PILOT_WIDTH_CLK))
            {
                ui8Result = 0;
            }
            else
            {
                ui8Result = 1;
            }
        }
    }
    return ui8Result;
}

void am_device_t5838_write_bit(uint8_t ui8Value)
{
    am_hal_gpio_state_write(AM_T5838_THSEL_PIN, AM_HAL_GPIO_OUTPUT_SET);
    if(ui8Value == 0)
    {
        am_device_t5838_clk_cycle(T5838_WRITE_ZERO_CLK);
    }
    else
    {
        am_device_t5838_clk_cycle(T5838_WRITE_ONE_CLK);
    }
}

void am_device_t5838_write_byte(uint8_t ui8Addr, uint8_t ui8Value)
{
    am_device_t5838_start_cmd();
    am_device_t5838_space_cmd();

    for(int i = 7; i >= 0; i--)
    {
        am_device_t5838_write_bit((am_hal_gpio_write_type_e)((0xA6 >> i) & 0x1));
        am_device_t5838_space_cmd();
    }

    for(int i = 7; i >= 0; i--)
    {
        am_device_t5838_write_bit((am_hal_gpio_write_type_e)((ui8Addr >> i) & 0x1));
        am_device_t5838_space_cmd();
    }

    for(int i = 7; i >= 0; i--)
    {
        am_device_t5838_write_bit((am_hal_gpio_write_type_e)((ui8Value >> i) & 0x1));
        am_device_t5838_space_cmd();
    }

    am_device_t5838_stop_cmd();
}

uint8_t am_device_t5838_read_byte(uint8_t ui8Addr)
{
    uint8_t ui8Value = 0;

    am_device_t5838_start_cmd();
    am_device_t5838_space_cmd();

    for(int i = 7; i >= 0; i--)
    {
        am_device_t5838_write_bit((am_hal_gpio_write_type_e)((0xA7 >> i) & 0x1));
        am_device_t5838_space_cmd();
    }

    for(int i = 7; i >= 0; i--)
    {
        am_device_t5838_write_bit((am_hal_gpio_write_type_e)((ui8Addr >> i) & 0x1));
        am_device_t5838_space_cmd();
    }

    am_device_t5838_clk_cycle(3);

    for(int i = 7; i >= 0; i--)
    {
        ui8Value |= (am_device_t5838_read_bit() << i);
//        am_util_stdio_printf("%d = %d\n", i, ui8Value);
        //am_device_t5838_space_cmd();
    }

    am_device_t5838_stop_cmd();

    return ui8Value;
}
void am_device_t5838_dump_reg(void)
{
    am_util_stdio_printf("0x29 = 0x%x\n", am_device_t5838_read_byte(0x29));
    am_util_stdio_printf("0x2A = 0x%x\n", am_device_t5838_read_byte(0x2A));
    am_util_stdio_printf("0x2B = 0x%x\n", am_device_t5838_read_byte(0x2B));
    am_util_stdio_printf("0x2C = 0x%x\n", am_device_t5838_read_byte(0x2C));
    am_util_stdio_printf("0x2D = 0x%x\n", am_device_t5838_read_byte(0x2D));
    am_util_stdio_printf("0x2E = 0x%x\n", am_device_t5838_read_byte(0x2E));
    am_util_stdio_printf("0x2F = 0x%x\n", am_device_t5838_read_byte(0x2F));
    am_util_stdio_printf("0x30 = 0x%x\n", am_device_t5838_read_byte(0x30));
    am_util_stdio_printf("0x31 = 0x%x\n", am_device_t5838_read_byte(0x31));
    am_util_stdio_printf("0x32 = 0x%x\n", am_device_t5838_read_byte(0x32));
    am_util_stdio_printf("0x33 = 0x%x\n", am_device_t5838_read_byte(0x33));
    am_util_stdio_printf("0x35 = 0x%x\n", am_device_t5838_read_byte(0x35));
    am_util_stdio_printf("0x36 = 0x%x\n", am_device_t5838_read_byte(0x36));
}

void am_device_t5838_aad_init(void)
{
#if defined (AM_PART_APOLLO3_API)
    am_hal_gpio_pincfg_t sGpioConfig = g_AM_HAL_GPIO_OUTPUT_WITH_READ;
#else
    am_hal_gpio_pincfg_t sGpioConfig = am_hal_gpio_pincfg_output_with_read;
#endif

    //sGpioConfig.GP.cfg_b.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_0P5X;
    am_hal_gpio_pinconfig(AM_T5838_THSEL_PIN, sGpioConfig);

#if defined (AM_PART_APOLLO3_API)
    sGpioConfig = g_AM_HAL_GPIO_OUTPUT;
#else
    sGpioConfig = am_hal_gpio_pincfg_output;
#endif

    //sGpioConfig.GP.cfg_b.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_0P5X;
    am_hal_gpio_pinconfig(T5838_PDM_CLK, sGpioConfig);

    //AM_CRITICAL_BEGIN;
    am_device_t5838_write_byte(0x5C, 0x0);
    am_device_t5838_write_byte(0x3E, 0x0);
    am_device_t5838_write_byte(0x6F, 0x0);
    am_device_t5838_write_byte(0x3B, 0x0);
    am_device_t5838_write_byte(0x4C, 0x0);

#if T5838_AAD_A
    am_device_t5838_write_byte(0x29, 0x0);	

    am_device_t5838_write_byte(0x35, 0x1);
    am_device_t5838_write_byte(0x36, 0x4);          // AAD Threshold 0x4 = 70dB SPL
//    am_device_t5838_write_byte(0x36, 0x6);          // AAD Threshold 0x6 = 75dB SPL
//    am_device_t5838_write_byte(0x36, 0x8);          // AAD Threshold 0x8 = 80dB SPL
//    am_device_t5838_write_byte(0x36, 0xE);          // AAD Threshold 0xE = 95dB SPL

    am_device_t5838_write_byte(0x29, 0x8);
#endif

#if T5838_AAD_D1
    am_device_t5838_write_byte(0x29, 0x0);	

    am_device_t5838_write_byte(0x2A, 0x0); 
//    am_device_t5838_write_byte(0x2B, 0xCF);     // Floor : 66~67 dB?
    am_device_t5838_write_byte(0x2B, 0x37);     // Floor : 55 dB
    am_device_t5838_write_byte(0x2E, 0x20);
    am_device_t5838_write_byte(0x2F, 0xF0);         

    am_device_t5838_write_byte(0x30, 0xFF);
    am_device_t5838_write_byte(0x31, 0x59);
    am_device_t5838_write_byte(0x32, 0x4E);
    am_device_t5838_write_byte(0x33, 0x64);
    am_device_t5838_write_byte(0x2C, 0x32);

    am_device_t5838_write_byte(0x29, 0x1);	

    //am_device_t5838_clk_cycle(T5838_IDLE_PERIOD_CLK * 10);
    sGpioConfig = g_AM_BSP_GPIO_PDM0_CLK;
    //sGpioConfig.GP.cfg_b.eDriveStrength = AM_HAL_GPIO_PIN_DRIVESTRENGTH_0P5X;
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_CLK, sGpioConfig);
    // To running AAD D1 mode, PDM clock will provided within around 70 us.
#endif

#if T5838_AAD_D2
    am_device_t5838_write_byte(0x29, 0x0);

    am_device_t5838_write_byte(0x2A, 0x0);      // Floor [12:8]
//    am_device_t5838_write_byte(0x2B, 0xCF);     // Floor [7:0]  0xCF = around 67 dB
    am_device_t5838_write_byte(0x2B, 0x18);     // Floor [7:0]  0x18 = 50.3 dB
    am_device_t5838_write_byte(0x2E, 0x20);     // Relative Pulse Min [7:0] 0x20
                                                // Relative Pulse Min [11:0] 0x20 = 4 ms
//    am_device_t5838_write_byte(0x2F, 0xF0);     // Absolute Pulse Min [11:8] 0xF Relative Pulse Min[11:8] 0x0
    am_device_t5838_write_byte(0x2F, 0x00);     // Absolute Pulse Min [11:8] 0x0 Relative Pulse Min[11:8] 0x0
    am_device_t5838_write_byte(0x30, 0xFF);     // Absolute Pulse Min [7:0] 0xFF
                                                // Absolute Pulse Min [11:0] 0xFF = 32 ms
//    am_device_t5838_write_byte(0x31, 0x59);     // Absolute THreshold [7:0] 0xE59 = N/A (out of spec)
//    am_device_t5838_write_byte(0x32, 0x4E);     // Absolute THreshold [11:8]
    am_device_t5838_write_byte(0x31, 0x69);     // Absolute THreshold [7:0] 0x5A
    am_device_t5838_write_byte(0x32, 0x41);     // Absolute THreshold [11:8] 0x0
                                                // Absolute THreshold [11:0] 0x5A = 60.8 dB
    am_device_t5838_write_byte(0x33, 0x64);     // Relative Threshold [7:0] 0x64 = +12 dB
    am_device_t5838_write_byte(0x2C, 0x32);     // Reserved

    am_device_t5838_write_byte(0x29, 0x2);	
#endif
    //am_device_t5838_dump_reg();
    //AM_CRITICAL_END;
}

void am_device_t5838_aad_enable(void)
{
#if T5838_AAD_A || T5838_AAD_D2
    am_device_t5838_clk_cycle(T5838_IDLE_PERIOD_CLK);
#endif
}

void am_device_t5838_aad_disable(void)
{
    AM_CRITICAL_BEGIN;
#if 0
    am_device_t5838_write_byte(0x5C, 0x0);
    am_device_t5838_write_byte(0x3E, 0x0);
    am_device_t5838_write_byte(0x6F, 0x0);
    am_device_t5838_write_byte(0x3B, 0x0);
    am_device_t5838_write_byte(0x4C, 0x0);
#endif

#if 0 //T5838_AAD_A || T5838_AAD_D2
    am_device_t5838_write_byte(0x29, 0x0);
#endif
    AM_CRITICAL_END;
}

#endif // configUSE_AAD

