#include <xs1.h>
#include <assert.h>
#include <platform.h>
#include <print.h>
#include <stdio.h>
#include "i2c.h"
#include "xua.h"
#define DEBUG_UNIT AUDIO_CFG
#define DEBUG_PRINT_ENABLE_AUDIO_CFG 1
#include "debug_print.h"


// TLV320DAC3101 Device I2C Address
#define DAC3101_I2C_DEVICE_ADDR 0x18

// TLV320DAC3101 Register Addresses
// Page 0
#define DAC3101_PAGE_CTRL     0x00 // Register 0 - Page Control
#define DAC3101_SW_RST        0x01 // Register 1 - Software Reset
#define DAC3101_CLK_GEN_MUX   0x04 // Register 4 - Clock-Gen Muxing
#define DAC3101_PLL_P_R       0x05 // Register 5 - PLL P and R Values
#define DAC3101_PLL_J         0x06 // Register 6 - PLL J Value
#define DAC3101_PLL_D_MSB     0x07 // Register 7 - PLL D Value (MSB)
#define DAC3101_PLL_D_LSB     0x08 // Register 8 - PLL D Value (LSB)
#define DAC3101_NDAC_VAL      0x0B // Register 11 - NDAC Divider Value
#define DAC3101_MDAC_VAL      0x0C // Register 12 - MDAC Divider Value
#define DAC3101_DOSR_VAL_LSB  0x0E // Register 14 - DOSR Divider Value (LS Byte)
#define DAC3101_CLKOUT_MUX    0x19 // Register 25 - CLKOUT MUX
#define DAC3101_CLKOUT_M_VAL  0x1A // Register 26 - CLKOUT M_VAL
#define DAC3101_CODEC_IF      0x1B // Register 27 - CODEC Interface Control
#define DAC3101_DAC_DAT_PATH  0x3F // Register 63 - DAC Data Path Setup
#define DAC3101_DAC_VOL       0x40 // Register 64 - DAC Vol Control
#define DAC3101_DACL_VOL_D    0x41 // Register 65 - DAC Left Digital Vol Control
#define DAC3101_DACR_VOL_D    0x42 // Register 66 - DAC Right Digital Vol Control
#define DAC3101_GPIO1_IO      0x33 // Register 51 - GPIO1 In/Out Pin Control
// Page 1
#define DAC3101_HP_DRVR       0x1F // Register 31 - Headphone Drivers
#define DAC3101_SPK_AMP       0x20 // Register 32 - Class-D Speaker Amp
#define DAC3101_HP_DEPOP      0x21 // Register 33 - Headphone Driver De-pop
#define DAC3101_DAC_OP_MIX    0x23 // Register 35 - DAC_L and DAC_R Output Mixer Routing
#define DAC3101_HPL_VOL_A     0x24 // Register 36 - Analog Volume to HPL
#define DAC3101_HPR_VOL_A     0x25 // Register 37 - Analog Volume to HPR
#define DAC3101_SPKL_VOL_A    0x26 // Register 38 - Analog Volume to Left Speaker
#define DAC3101_SPKR_VOL_A    0x27 // Register 39 - Analog Volume to Right Speaker
#define DAC3101_HPL_DRVR      0x28 // Register 40 - Headphone Left Driver
#define DAC3101_HPR_DRVR      0x29 // Register 41 - Headphone Right Driver
#define DAC3101_SPKL_DRVR     0x2A // Register 42 - Left Class-D Speaker Driver
#define DAC3101_SPKR_DRVR     0x2B // Register 43 - Right Class-D Speaker Driver

// TLV320DAC3101 easy register access defines
#define DAC3101_REGWRITE(reg, val) {i_i2c.write_reg(DAC3101_I2C_DEVICE_ADDR, reg, val);}


// Nominal setting is ref div = 25, fb_div = 1024, op_div = 2
// PCF Freq 0.96MHz

enum clock_nudge{
    PLL_SLOWER = -1,
    PLL_NOMINAL = 0,
    PLL_FASTER = 1
};

//These steps provide just under +-0.1% frequency jumps
#define PLL_LOW  0xC003FE18 // This is 3.069MHz
#define PLL_NOM  0xC003FF18 // This is 3.072MHz
#define PLL_HIGH 0xC0040018 // This is 3.075MHz

on tile[0]: out port p_leds = XS1_PORT_4F;

//Note use of no_ack write to prevent backpressure. There is enough buffering to
//store both writes so we can move on without blocking
void pll_nudge(int nudge) {
    if (nudge > 0){
        write_sswitch_reg_no_ack(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_HIGH);
        p_leds <: 0x02; //LED B
    }
    else if (nudge < 0){
        write_sswitch_reg_no_ack(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_LOW);
        p_leds <: 0x01; //LED A

    }
    else {
        p_leds <: 0x0;
    }
    write_sswitch_reg_no_ack(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_NOM);
}

void setup_audio_gpio(out port p_gpio){
  // Reset DAC and disable MUTE
  p_gpio <: 0x0;
  delay_milliseconds(1);
  p_gpio <: 0x1;
  delay_milliseconds(1);
}

void AudioHwConfigure(unsigned samFreq, client i2c_master_if i_i2c)
{

    // Wait for 2ms because we apply reset for 1ms from other tile
    delay_milliseconds(2);

    // Set register page to 0
    DAC3101_REGWRITE(DAC3101_PAGE_CTRL, 0x00);
    // Initiate SW reset (PLL is powered off as part of reset)
    DAC3101_REGWRITE(DAC3101_SW_RST, 0x01);

    // so I've got 24MHz in to PLL, I want 24.576MHz or 22.5792MHz out.

    // I will always be using fractional-N (D != 0) so we must set R = 1
    // PLL_CLKIN/P must be between 10 and 20MHz so we must set P = 2

    // PLL_CLK = CLKIN * ((RxJ.D)/P)
    // We know R = 1, P = 2.
    // PLL_CLK = CLKIN * (J.D / 2)

    // For 24.576MHz:
    // J = 8
    // D = 1920
    // So PLL_CLK = 24 * (8.192/2) = 24 x 4.096 = 98.304MHz
    // Then:
    // NDAC = 4
    // MDAC = 4
    // DOSR = 128
    // So:
    // DAC_CLK = PLL_CLK / 4 = 24.576MHz.
    // DAC_MOD_CLK = DAC_CLK / 4 = 6.144MHz.
    // DAC_FS = DAC_MOD_CLK / 128 = 48kHz.

    // For 22.5792MHz:
    // J = 7
    // D = 5264
    // So PLL_CLK = 24 * (7.5264/2) = 24 x 3.7632 = 90.3168MHz
    // Then:
    // NDAC = 4
    // MDAC = 4
    // DOSR = 128
    // So:
    // DAC_CLK = PLL_CLK / 4 = 22.5792MHz.
    // DAC_MOD_CLK = DAC_CLK / 4 = 5.6448MHz.
    // DAC_FS = DAC_MOD_CLK / 128 = 44.1kHz.

#if XUA_ADAPTIVE
    //Set nominal clock speed on PLL
    write_sswitch_reg(get_tile_id(tile[0]), XS1_SSWITCH_PLL_CTL_NUM, PLL_NOM);

    // We are assuming 48kHz family only and we generate MCLK in the DAC from BLCK supplied by XCORE
    // Set PLL J Value to 8
    DAC3101_REGWRITE(DAC3101_PLL_J, 0x08);
    // Set PLL D to 0 ...
    // Set PLL D MSB Value to 0x00
    DAC3101_REGWRITE(DAC3101_PLL_D_MSB, 0x00);
    // Set PLL D LSB Value to 0x00
    DAC3101_REGWRITE(DAC3101_PLL_D_LSB, 0x00);

    delay_milliseconds(1);

    // Set PLL_CLKIN = BCLK (device pin), CODEC_CLKIN = PLL_CLK (generated on-chip)
    DAC3101_REGWRITE(DAC3101_CLK_GEN_MUX, 0x07);

    // Set PLL P=1 and R=4 values and power up.
    DAC3101_REGWRITE(DAC3101_PLL_P_R, 0x94);
    // Set NDAC clock divider to 4 and power up.
    DAC3101_REGWRITE(DAC3101_NDAC_VAL, 0x84);
    // Set MDAC clock divider to 4 and power up.
    DAC3101_REGWRITE(DAC3101_MDAC_VAL, 0x84);
    // Set OSR clock divider to 128.
    DAC3101_REGWRITE(DAC3101_DOSR_VAL_LSB, 0x80);

#else
    /* Sample frequency dependent register settings */
    if ((samFreq % 11025) == 0)
    {
        // MCLK = 22.5792MHz (44.1,88.2,176.4kHz)
        // Set PLL J Value to 7
        DAC3101_REGWRITE(DAC3101_PLL_J, 0x07);
        // Set PLL D to 5264 ... (0x1490)
        // Set PLL D MSB Value to 0x14
        DAC3101_REGWRITE(DAC3101_PLL_D_MSB, 0x14);
        // Set PLL D LSB Value to 0x90
        DAC3101_REGWRITE(DAC3101_PLL_D_LSB, 0x90);

    }
    else if ((samFreq % 8000) == 0)
    {
        // MCLK = 24.576MHz (48,96,192kHz)
        // Set PLL J Value to 8
        DAC3101_REGWRITE(DAC3101_PLL_J, 0x08);
        // Set PLL D to 1920 ... (0x780)
        // Set PLL D MSB Value to 0x07
        DAC3101_REGWRITE(DAC3101_PLL_D_MSB, 0x07);
        // Set PLL D LSB Value to 0x80
        DAC3101_REGWRITE(DAC3101_PLL_D_LSB, 0x80);
    }
    else
    {
        //debug_printf("Unrecognised sample freq of %d in ConfigCodec\n", samFreq);
    }

    delay_milliseconds(1);

    // Set PLL_CLKIN = MCLK (device pin), CODEC_CLKIN = PLL_CLK (generated on-chip)
    DAC3101_REGWRITE(DAC3101_CLK_GEN_MUX, 0x03);

    // Set PLL P and R values and power up.
    DAC3101_REGWRITE(DAC3101_PLL_P_R, 0xA1);
    // Set NDAC clock divider to 4 and power up.
    DAC3101_REGWRITE(DAC3101_NDAC_VAL, 0x84);
    // Set MDAC clock divider to 4 and power up.
    DAC3101_REGWRITE(DAC3101_MDAC_VAL, 0x84);
    // Set OSR clock divider to 128.
    DAC3101_REGWRITE(DAC3101_DOSR_VAL_LSB, 0x80);

#endif

    // Set CLKOUT Mux to DAC_CLK
    DAC3101_REGWRITE(DAC3101_CLKOUT_MUX, 0x04);
    // Set CLKOUT M divider to 1 and power up.
    DAC3101_REGWRITE(DAC3101_CLKOUT_M_VAL, 0x81);
    // Set GPIO1 output to come from CLKOUT output.
    DAC3101_REGWRITE(DAC3101_GPIO1_IO, 0x10);

    // Set CODEC interface mode: I2S, 24 bit, slave mode (BCLK, WCLK both inputs).
    DAC3101_REGWRITE(DAC3101_CODEC_IF, 0x20);
    // Set register page to 1
    DAC3101_REGWRITE(DAC3101_PAGE_CTRL, 0x01);
    // Program common-mode voltage to mid scale 1.65V.
    DAC3101_REGWRITE(DAC3101_HP_DRVR, 0x14);
    // Program headphone-specific depop settings.
    // De-pop, Power on = 800 ms, Step time = 4 ms
    DAC3101_REGWRITE(DAC3101_HP_DEPOP, 0x4E);
    // Program routing of DAC output to the output amplifier (headphone/lineout or speaker)
    // LDAC routed to left channel mixer amp, RDAC routed to right channel mixer amp
    DAC3101_REGWRITE(DAC3101_DAC_OP_MIX, 0x44);
    // Unmute and set gain of output driver
    // Unmute HPL, set gain = 0 db
    DAC3101_REGWRITE(DAC3101_HPL_DRVR, 0x06);
    // Unmute HPR, set gain = 0 dB
    DAC3101_REGWRITE(DAC3101_HPR_DRVR, 0x06);
    // Unmute Left Class-D, set gain = 12 dB
    DAC3101_REGWRITE(DAC3101_SPKL_DRVR, 0x0C);
    // Unmute Right Class-D, set gain = 12 dB
    DAC3101_REGWRITE(DAC3101_SPKR_DRVR, 0x0C);
    // Power up output drivers
    // HPL and HPR powered up
    DAC3101_REGWRITE(DAC3101_HP_DRVR, 0xD4);
    // Power-up L and R Class-D drivers
    DAC3101_REGWRITE(DAC3101_SPK_AMP, 0xC6);
    // Enable HPL output analog volume, set = -9 dB
    DAC3101_REGWRITE(DAC3101_HPL_VOL_A, 0x92);
    // Enable HPR output analog volume, set = -9 dB
    DAC3101_REGWRITE(DAC3101_HPR_VOL_A, 0x92);
    // Enable Left Class-D output analog volume, set = -9 dB
    DAC3101_REGWRITE(DAC3101_SPKL_VOL_A, 0x92);
    // Enable Right Class-D output analog volume, set = -9 dB
    DAC3101_REGWRITE(DAC3101_SPKR_VOL_A, 0x92);

    delay_milliseconds(100);

    // Power up DAC
    // Set register page to 0
    DAC3101_REGWRITE(DAC3101_PAGE_CTRL, 0x00);
    // Power up DAC channels and set digital gain
    // Powerup DAC left and right channels (soft step enabled)
    DAC3101_REGWRITE(DAC3101_DAC_DAT_PATH, 0xD4);
    // DAC Left gain = 0dB
    DAC3101_REGWRITE(DAC3101_DACL_VOL_D, 0x00);
    // DAC Right gain = 0dB
    DAC3101_REGWRITE(DAC3101_DACR_VOL_D, 0x00);
    // Unmute digital volume control
    // Unmute DAC left and right channels
    DAC3101_REGWRITE(DAC3101_DAC_VOL, 0x00);

    i_i2c.shutdown();
}


//These are here just to silence compiler warnings about unimplemented xua callbacks (not needed in xua lite)
void AudioHwInit(){}
void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC){}
