/*******************************************************************************
 *
 *      A U D I O   R E C O R D E R    U I  D R I V E R
 *
 *      Copyright A Levido 2013 - All Rights Reserved
 *
 ******************************************************************************/
#include "cdcrtc.h"
#include <plib.h>
#include "system.h"

#define CDC_SEL_HI              PORTSetBits(IOPORT_D, BIT_6)
#define CDC_SEL_LO              PORTClearBits(IOPORT_D, BIT_6)



#define UI_SAMPLE_RATE          100       // samples per sec
#define UI_REPEAT_RATE          20        // in units of samples
#define UI_REPEAT_DELAY         100       // in units of samples

/* Globals */
volatile UINT32 uihistory;
volatile UINT32 uicounter;
volatile UINT32 uistate;
volatile UINT32 uibutton;
enum { BTN_NONE, BTN_L, BTN_C, BTN_R, BTN_U, BTN_D };
enum { BTN_UP, BTN_DN, BTN_DLY, BTN_RPT };



/* CODEC control */
const INT8 lineGain[][9] =
    {
        "-34.5 dB", "-33.0 dB", "-31.5 dB", "-30.0 dB", "-28.5 dB", "-27.0 dB",
        "-25.5 dB", "-24.0 dB", "-22.5 dB", "-21.0 dB", "-19.5 dB", "-18.0 dB",
        "-16.5 dB", "-15.0 dB", "-13.5 dB", "-12.0 dB", "-10.5 dB", "-09.0 dB",
        "-07.5 dB", "-06.0 dB", "-04.5 dB", "-03.0 dB", "-01.5 dB", "+00.0 dB",
        "+01.5 dB", "+03.0 dB", "+04.5 dB", "+06.0 dB", "+07.5 dB", "+09.0 dB",
        "+10.5 dB", "+12.0 dB"
    };
const INT8 volGain[][7] =
    {
        "-73 dB", "-72 dB", "-71 dB", "-70 dB", "-69 dB", "-68 dB", "-67 dB",
        "-66 dB", "-65 dB", "-64 dB", "-63 dB", "-62 dB", "-61 dB", "-60 dB",
        "-59 dB", "-58 dB", "-57 dB", "-56 dB", "-55 dB", "-54 dB", "-53 dB",
        "-52 dB", "-51 dB", "-50 dB", "-49 dB", "-48 dB", "-47 dB", "-46 dB",
        "-45 dB", "-44 dB", "-43 dB", "-42 dB", "-41 dB", "-40 dB", "-39 dB",
        "-38 dB", "-37 dB", "-36 dB", "-35 dB", "-34 dB", "-33 dB", "-32 dB",
        "-31 dB", "-30 dB", "-29 dB", "-28 dB", "-27 dB", "-26 dB", "-25 dB",
        "-24 dB", "-23 dB", "-22 dB", "-21 dB", "-20 dB", "-19 dB", "-18 dB",
        "-17 dB", "-16 dB", "-15 dB", "-14 dB", "-13 dB", "-12 dB", "-11 dB",
        "-10 dB", "-09 dB", "-08 dB", "-07 dB", "-06 dB", "-05 dB", "-04 dB",
        "-03 dB", "-02 dB", "-01 dB", "+00 dB", "+01 dB", "+02 dB", "+03 dB",
        "+04 dB", "+05 dB", "+06 dB"
    };
const struct {
    const INT8 label[11];
    const UINT32 clkdiv;
    const UINT32 smplrate;
    const UINT32 baserate;
    const UINT32 actualrate;
}rates[] =
{
    { "8 kbps", 0, 0x3, 0, 8000 },
    { "16 kbps", 1, 0x6, 0, 16000 },
    { "22.05 kbp", 1, 0x8, 1, 22050 },
    { "24 kbps", 1, 0x0, 0, 24000 },
    { "32 kbps", 0, 0x6, 0, 32000 },
    { "44.1 kbps", 0, 0x8, 1, 44100 },
    { "48 kbps", 0, 0x0, 0, 48000 },
    { "88.2 kbps", 0, 0xf, 1, 88200 },
    { "96 kbps", 0, 0x7, 0, 96000 }
};

////// Codec Data Structure ////////////////////////////////////////////////////
union{
    struct{
        UINT level      : 5;
        UINT            : 2;
        UINT mute       : 1;
        UINT sim        : 1;
        UINT addr       : 7;
    };
    UINT all;
}volatile lline;

volatile union{
    struct{
        UINT level      : 5;
        UINT            : 2;
        UINT mute       : 1;
        UINT sim        : 1;
        UINT addr       : 7;
    };
    UINT all;
}rline;

union{
    struct{
        UINT level      : 7;
        UINT zcross     : 1;
        UINT sim        : 1;
        UINT addr       : 7;
    };
    UINT all;
}volatile lphone;

union{
    struct{
        UINT level      : 7;
        UINT zcross     : 1;
        UINT sim        : 1;
        UINT addr       : 7;
    };
    UINT all;
}volatile rphone;

union{
    struct{
        UINT micboost   : 1;
        UINT micmute    : 1;
        UINT micselect  : 1;
        UINT bypass     : 1;
        UINT dacselect  : 1;
        UINT sidetone   : 1;
        UINT sideatten  : 2;
        UINT            : 1;
        UINT addr       : 7;
    };
    UINT all;
}volatile analog;

union{
    struct{
        UINT adchipass  : 1;
        UINT deemphasis : 2;
        UINT dacmute    : 1;
        UINT            : 5;
        UINT addr       : 7;
    };
    UINT all;
}volatile digital;

union{
    struct{
        UINT lineoff    : 1;
        UINT micoff     : 1;
        UINT adcoff     : 1;
        UINT dacoff     : 1;
        UINT outoff     : 1;
        UINT oscoff     : 1;
        UINT clkoff     : 1;
        UINT deviceoff  : 1;
        UINT            : 1;
        UINT addr       : 7;
    };
    UINT all;
} volatile power;

union{
    struct{
        UINT format     : 2;
        UINT length     : 2;
        UINT lrphase    : 1;
        UINT lrswap     : 1;
        UINT master     : 1;
        UINT            : 2;
        UINT addr       : 7;
    };
    UINT all;
}volatile data;

union{
    struct{
        UINT usbmode    : 1;
        UINT bosr       : 1;
        UINT srate      : 4;
        UINT clkin      : 1;
        UINT clkout     : 1;
        UINT            : 1;
        UINT addr       : 7;
    };
    UINT all;
}volatile rate;

union{
    struct{
        UINT active     : 1;
        UINT            : 8;
        UINT addr       : 7;
    };
    UINT all;
}volatile activate;

union{
    struct{
        UINT rst        : 9;
        UINT addr       : 7;
    };
    UINT all;
}volatile reset;

volatile UINT32 usrMute;
volatile UINT32 sysMute;
volatile UINT32 volume;
volatile UINT32 linelevel;
volatile UINT32 samplerate;



void outCDC (UINT32 data)
{  
    CDC_SEL_LO;
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    SPI3BUF = data;
    while(!SPI3STATbits.SPIRBF);
    data = SPI3BUF;
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    asm volatile("nop");
    CDC_SEL_HI;
}


/* API Functions **************************************************************/
void cdcInit(void)
{
    UINT32 i;
    
    /* Shared Resources  */ 
    CDC_SEL_HI;

    SPI3CONbits.MSTEN = 1;              // Master mode
    SPI3CONbits.MODE16 = 1;             // 16 bit

    SPI3CONbits.CKE = 1;                //
    SPI3CONbits.CKP = 0;                //
    SPI3BRG = 1;                        // F = FPB / (2 * (BRG + 1)
    SPI3CONbits.ON = 1;

    
    /* Set up the codec */
    outCDC(0x1e00);

    lline.level = 0b10111;
    lline.mute = 0;
    lline.sim = 1;
    lline.addr = 0;
    rline.level = 0b10111;
    rline.mute = 0;
    rline.sim = 1;
    rline.addr = 1;
    lphone.level = 105;
    lphone.zcross = 1;
    lphone.sim = 1;
    lphone.addr = 2;
    rphone.level = 105;
    rphone.zcross = 1;
    rphone.sim = 1;
    rphone.addr = 3;
    analog.micboost = 0;
    analog.micmute = 0;
    analog.micselect = 0;
    analog.bypass = 0;
    analog.dacselect = 1;
    analog.sidetone = 0;
    analog.sideatten = 0;
    analog.addr = 4;
    digital.adchipass = 0;
    digital.deemphasis = 0;//2;
    digital.dacmute = 1;
    digital.addr = 5;
    power.lineoff = 0;
    power.micoff = 0;
    power.adcoff = 0;
    power.dacoff = 0;
    power.outoff = 0;
    power.oscoff = 0;
    power.clkoff = 0;
    power.deviceoff = 0;
    power.addr = 6;
    data.format = 3;
    data.length = 0;
    data.lrphase = 1;
    data.lrswap = 0;
    data.master = 1;
    data.addr = 7;
    rate.usbmode = 1;
    rate.bosr = 0,
    rate.srate = 0;
    rate.clkin = 0;
    rate.clkout = 0;
    rate.addr = 8;
    activate.active = 1;
    activate.addr = 9;
    reset.rst = 0;
    reset.addr = 0xf;
    volume = 101;
    linelevel = 23;
    sysMute = 1;
    usrMute = 0;


    outCDC(lline.all);
//    outCDC(rline.all);
    outCDC(lphone.all);
//    outCDC(rphone.all);
    outCDC(analog.all);
    outCDC(digital.all);
    outCDC(power.all);
    outCDC(data.all);
    outCDC(rate.all);
    outCDC(activate.all);
}

void cdcShutdown()
{
    power.deviceoff = 1;
    power.lineoff = 1;
    power.micoff = 1;
    power.adcoff = 1;
    power.dacoff = 1;
    power.outoff = 1;
    power.oscoff = 1;
    power.clkoff = 1;
    power.addr = 6;
    outCDC(power.all);
}

void cdcWakeup()
{
    power.deviceoff = 0;
    power.lineoff = 0;
    power.micoff = 0;
    power.adcoff = 0;
    power.dacoff = 0;
    power.outoff = 0;
    power.oscoff = 0;
    power.clkoff = 0;
    power.addr = 6;
    outCDC(power.all);
}


/* CODEC Functions ************************************************************/
#define MAX_LINE_LVL        31
#define MIN_LINE_LVL         0

void cdcRestart(void)
{
    outCDC(reset.all);
    outCDC(lline.all);
//    outCDC(rline.all);
    outCDC(lphone.all);
//    outCDC(rphone.all);
    outCDC(analog.all);
    outCDC(digital.all);
    outCDC(power.all);
    outCDC(data.all);
    outCDC(rate.all);
    outCDC(activate.all);
}
INT8* cdcIncLineLevel(void)
{
    linelevel = linelevel < MAX_LINE_LVL ? linelevel + 1 : MAX_LINE_LVL;
    lline.level = linelevel;
    outCDC(lline.all);
    return(INT8*)(&lineGain[linelevel][0]);
}

INT8* cdcDecLineLevel(void)
{
    linelevel = linelevel > MIN_LINE_LVL ? linelevel - 1 : MIN_LINE_LVL;
    lline.level = linelevel;
    outCDC(lline.all);
    return(INT8*)(&lineGain[linelevel][0]);
}

INT8* cdcSetMicBoost(UINT32 boost)
{
    analog.micboost = boost;
    outCDC(analog.all);
    return boost ? "+20 dB" : "0 dB";
}

UINT32 cdcGetMicBoost(void)
{
    return analog.micboost;
}

void cdcSetInputMute(UINT32 mute)
{
    analog.micmute = mute;
    lline.mute = mute;
    outCDC(analog.all);
    outCDC(lline.all);
}

#define MAX_VOL_LVL       127
#define MIN_VOL_LVL        48
INT8* cdcIncVolume(void)
{
    volume = volume < MAX_VOL_LVL ? volume + 1 : MAX_VOL_LVL;
    lphone.level = volume;
    if(digital.dacmute == 0)
        outCDC(lphone.all);
    return(INT8*)(&volGain[volume - MIN_VOL_LVL][0]);
}

INT8* cdcDecVolume(void)
{
    volume = volume > MIN_VOL_LVL ? volume - 1 : MIN_VOL_LVL;
    lphone.level = volume;
    if(digital.dacmute == 0)
        outCDC(lphone.all);
    return(INT8*)(&volGain[volume - MIN_VOL_LVL][0]);
}

void cdcSetOutputMute(UINT32 mute)
{
    usrMute = mute;
    digital.dacmute = sysMute | usrMute;
    outCDC(digital.all);
    if(digital.dacmute == 0)
        outCDC(lphone.all);
}
void cdcSetDACMute(UINT32 mute)
{
    sysMute = mute;
    digital.dacmute = sysMute | usrMute;
    outCDC(digital.all);
    if(digital.dacmute == 0)
        outCDC(lphone.all);
}

INT8* cdcSetRate(UINT32 r)
{
    rate.clkin = rates[r].clkdiv;
    rate.bosr = rates[r].baserate;
    rate.srate = rates[r].smplrate;
    samplerate = rates[r].actualrate;
    outCDC(rate.all);
    return (INT8*)(&rates[r].label[0]);
}

int cdcGetSampleRate() {
    return samplerate;
}

INT8* cdcGetRateString(UINT32 r)
{
    return (INT8*)(&rates[r].label[0]);
}
void cdcSetBitDepth(UINT32 is32)
{
    data.length = is32 ? 3 : 0;
    outCDC(data.all);
}

void cdcSetSource(UINT32 mic)
{
    analog.micselect = mic ? 1 : 0;
    outCDC(analog.all);
}
UINT32 cdcGetSource(void)
{
    return analog.micselect;
}
void cdcSetLRSwap(UINT32 swap)
{
    data.lrswap = swap ? 1 : 0;
    outCDC(data.all);
}
void cdcSetDeemphasis(UINT32 demph)
{
    digital.deemphasis = demph;
    outCDC(digital.all);
}
void cdcSetMonitor(UINT32 monitor)
{
    if(monitor){
        if(analog.micselect){ analog.sidetone = 1; }
        else { analog.bypass = 1; }
    }
    else {
        analog.sidetone = 0;
        analog.bypass = 0;
    }
    outCDC(analog.all);
}

/* Real Time Clock ************************************************************/
void clkInitialise(void)
{
    RtccInit();
    while(RtccGetClkStat() != RTCC_CLK_ON);
    RtccSetTimeDate(0x00000000, 0x14010100);
    RtccSetAlarmRpt(RTCC_RPT_HALF_SEC);
    RtccChimeEnable();
    RtccAlarmEnable();
    INTSetVectorPriority(INT_RTCC_VECTOR,INT_PRIORITY_LEVEL_3);
    INTEnable(INT_RTCC, INT_ENABLED);
}
void clkShutdown()
{
    INTEnable(INT_RTCC, INT_DISABLED);
}
void clcWakeup()
{
    INTClearFlag(INT_RTCC);
    INTEnable(INT_RTCC, INT_ENABLED);
}
INT8* clkGetTimeString(INT8* timestring)
{
    LONG time;
    timestring[8] = 0;
    time = RtccGetTime();

    time = time >> 8;        // first 2 nibbles are unused
    timestring[7] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    timestring[6] = (char)(time & 0x0000000f) + '0';
    timestring[5] = TIME_SEPARATOR;
    time = time >> 4;
    timestring[4] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    timestring[3] = (char)(time & 0x0000000f) + '0';
    timestring[2] = TIME_SEPARATOR;
    time = time >> 4;
    timestring[1] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    timestring[0] = (char)(time & 0x0000000f) + '0';

    return(timestring);
}

INT8* clkGetDateString(INT8* datestring)
{
    LONG date;

    datestring[10] = 0;
    date = RtccGetDate();

    date = date >> 8;        // skip day of week
    datestring[9] = (char)(date & 0x0000000f) + '0';
    date = date >> 4;
    datestring[8] = (char)(date & 0x0000000f) + '0';
    datestring[7] = DATE_SEPARATOR;
    date = date >> 4;
    datestring[6] = (char)(date & 0x0000000f) + '0';
    date = date >> 4;
    datestring[5] = (char)(date & 0x0000000f) + '0';
    datestring[4] = DATE_SEPARATOR;
    date = date >> 4;
    datestring[3] = (char)(date & 0x0000000f) + '0';
    date = date >> 4;
    datestring[2] = (char)(date & 0x0000000f) + '0';
    datestring[1] = '0';
    datestring[0] = '2';

    return(datestring);
}

/* Return time and date at in a FAT packed 32 bit word
   3     2 2  2 2   1 1   1 1    0 0   0
   1     5 4  1 0   6 5   1 0    5 4   0
   ####### #### ##### ##### ###### #####
   Year   Month Day   Hour   Min   Sec/2
   from   1-12  1-31  0-23   0-59  0-29
   1980                                 */
DWORD get_fattime(void)
{
    LONG  fat;
    BYTE temp;
    rtccTime time;
    rtccDate date;

    time.l = RtccGetTime();
    date.l = RtccGetDate();

    temp = (time.sec & 0x0f) + 10 * (time.sec >> 4);
    fat = (LONG)temp / 2;
    temp = (time.min & 0x0f) + 10 * (time.min >> 4);
    fat |= (LONG)temp << 5;
    temp = (time.hour & 0x0f) + 10 * (time.hour >> 4);
    fat |= ((LONG)temp << 11);
    temp = (date.mday & 0x0f) + 10 * (date.mday >> 4);
    fat |= (LONG)temp << 16;
    temp = (date.mon & 0x0f) + 10 * (date.mon >> 4);
    fat |= (LONG)temp << 21;
    temp = (date.year & 0x0f) + 10 * (date.year >> 4);
    temp += 20; // RTCC uses 2000 as base, FAT uses 1980
    fat |= (LONG)temp << 25;

    return(fat);
}

void __ISR(_RTCC_VECTOR, IPL3AUTO) RTCCHandler(void)
{
    if(RTCCONbits.HALFSEC) { enqueueEvent(TICK, HALF_SEC, 0, 0); }
    else { enqueueEvent(TICK, ONE_SEC, 0, 0); }
    sdDetectStatus();
    INTClearFlag(INT_RTCC);
}

void clkAppendTimestamp(INT8* buff)
{
    INT8 string[15];
    LONG time;

    string[13] = 0;
    time = RtccGetTime();

    time = time >> 8;        // first 2 nibbles are unused
    string[12] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[11] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[10] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[9] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[8] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[7] = (char)(time & 0x0000000f) + '0';
    string[6] = '-';
    time = RtccGetDate();
    time = time >> 8;        // skip day of week
    string[5] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[4] = (char)(time & 0x0000000f) + '0';

    time = time >> 4;
    string[3] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[2] = (char)(time & 0x0000000f) + '0';

    time = time >> 4;
    string[1] = (char)(time & 0x0000000f) + '0';
    time = time >> 4;
    string[0] = (char)(time & 0x0000000f) + '0';

    strcat(buff, string);
}


#ifdef __cplusplus
}
#endif


