#include <includes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "player.h"
#include "spi1.h"


#define PLAYER_XRESET_SET()     {FIO2SET_bit.P2_11 = 1;}
#define PLAYER_XRESET_CLR()     {FIO2CLR_bit.P2_11 = 1;}

#define PLAYER_DREQ_HIGH()      (FIO1PIN_bit.P1_31)
#define PLAYER_DREQ_LOW()       (!FIO1PIN_bit.P1_31)

#define PLAYER_XDCS_SET()       {FIO2SET_bit.P2_12 = 1;}
#define PLAYER_XDCS_CLR()       {FIO2CLR_bit.P2_12 = 1;}

#define PLAYER_XCS_SET()       {FIO2SET_bit.P2_5 = 1;}
#define PLAYER_XCS_CLR()       {FIO2CLR_bit.P2_5 = 1;}


int player_conn = 0;


void delayMicroseconds(CPU_INT32U ms)
{
    while (ms--)
    {
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
        asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop"); asm(" nop");
    }
}

int await_data_request(void)
{
    CPU_INT32U time_stamp = OSTimeGet();
    while (PLAYER_DREQ_LOW())
    {
        if (OSTimeGet() - time_stamp > 1000)
        {
            return -1;
        }
    }
    return 0;
}

u_int16 ReadSci(u_int8 addr)
{
    u_int16 temp;
    spi1_getSem();
    if (await_data_request() != 0)
    {
        spi1_freeSem();
        return 0x0000;
    }
    PLAYER_XDCS_SET();
    PLAYER_XCS_CLR();
    delayMicroseconds(1);
    spi1_exchange(0x03);
    spi1_exchange(addr);
    delayMicroseconds(10);
    temp = spi1_exchange(0x00);
    temp <<= 8;
    temp += spi1_exchange(0x00);
    delayMicroseconds(1);
    PLAYER_XCS_SET();
    spi1_freeSem();
    return temp;  
}

void WriteSci(u_int8 addr, u_int16 data)
{
    spi1_getSem();
    
    if (await_data_request() != 0)
    {
        spi1_freeSem();
        return;
    }
        
    PLAYER_XDCS_SET();
    PLAYER_XCS_CLR();
    delayMicroseconds(1);       // tXCSS
    spi1_exchange(0x02);        // Write operation
    spi1_exchange(addr);        // Which register
    spi1_exchange(data >> 8);   // Send hi byte
    spi1_exchange(data & 0xff); // Send lo byte
    delayMicroseconds(1);       // tXCSH
    PLAYER_XCS_SET();

    await_data_request();
    spi1_freeSem();
}

int WriteSdi(const u_int8 *data, u_int16 bytes)
{
    spi1_getSem();
    PLAYER_XCS_SET();
    PLAYER_XDCS_CLR();

    while (bytes)
    {
        if (await_data_request() < 0)
        {
            PLAYER_XDCS_SET();
            spi1_freeSem();
            return -1;
        }
        delayMicroseconds(3);
        size_t chunk_length = min(bytes, SDI_MAX_TRANSFER_SIZE);
        bytes -= chunk_length;
        while (chunk_length--) spi1_exchange(*data++);
    }

    PLAYER_XDCS_SET();
    spi1_freeSem();
    return 0;
}


/*
  Read 32-bit increasing counter value from addr.
  Because the 32-bit value can change while reading it,
  read MSB's twice and decide which is the correct one.
*/
u_int32 ReadVS10xxMem32Counter(u_int16 addr) {
  u_int16 msbV1, lsb, msbV2;
  u_int32 res;

  WriteSci(SCI_WRAMADDR, addr+1);
  msbV1 = ReadSci(SCI_WRAM);
  WriteSci(SCI_WRAMADDR, addr);
  lsb = ReadSci(SCI_WRAM);
  msbV2 = ReadSci(SCI_WRAM);
  if (lsb < 0x8000U) {
    msbV1 = msbV2;
  }
  res = ((u_int32)msbV1 << 16) | lsb;
  
  return res;
}


/*
  Read 32-bit non-changing value from addr.
*/
u_int32 ReadVS10xxMem32(u_int16 addr) {
  u_int16 lsb;
  WriteSci(SCI_WRAMADDR, addr);
  lsb = ReadSci(SCI_WRAM);
  return lsb | ((u_int32)ReadSci(SCI_WRAM) << 16);
}


/*
  Read 16-bit value from addr.
*/
u_int16 ReadVS10xxMem(u_int16 addr) {
  WriteSci(SCI_WRAMADDR, addr);
  return ReadSci(SCI_WRAM);
}


/*
  Write 16-bit value to given VS10xx address
*/
void WriteVS10xxMem(u_int16 addr, u_int16 data) {
  WriteSci(SCI_WRAMADDR, addr);
  WriteSci(SCI_WRAM, data);
}

/*
  Write 32-bit value to given VS10xx address
*/
void WriteVS10xxMem32(u_int16 addr, u_int32 data) {
  WriteSci(SCI_WRAMADDR, addr);
  WriteSci(SCI_WRAM, (u_int16)data);
  WriteSci(SCI_WRAM, (u_int16)(data>>16));
}


/*

  Loads a plugin.

  This is a slight modification of the LoadUserCode() example
  provided in many of VLSI Solution's program packages.

*/
void LoadPlugin(const u_int16 *d, u_int16 len) {
  int i = 0;

  while (i<len) {
    unsigned short addr, n, val;
    addr = d[i++];
    n = d[i++];
    if (n & 0x8000U) { /* RLE run, replicate n samples */
      n &= 0x7FFF;
      val = d[i++];
      while (n--) {
        WriteSci(addr, val);
      }
    } else {           /* Copy run, copy n samples */
      while (n--) {
        val = d[i++];
        WriteSci(addr, val);
      }
    }
  }
}


void Set32(u_int8 *d, u_int32 n) {
  int i;
  for (i=0; i<4; i++) {
    *d++ = (u_int8)n;
    n >>= 8;
  }
}

void Set16(u_int8 *d, u_int16 n) {
  int i;
  for (i=0; i<2; i++) {
    *d++ = (u_int8)n;
    n >>= 8;
  }
}
/*

  Hardware Initialization for VS1053.

  
*/
int playerInitHardware(void) {
  /* Write here your microcontroller code which puts VS10xx in hardware
     reset anc back (set xRESET to 0 for at least a few clock cycles,
     then to 1). */

  // (XRESET) модуля
  PINSEL4_bit.P2_11 = 0x0;
  PINMODE4_bit.P2_11 = 0;
  FIO2DIR_bit.P2_11 = 1;
  FIO2MASK_bit.P2_11 = 0;
  PLAYER_XRESET_SET();

  // (XDCS) модуля
  PINSEL4_bit.P2_12 = 0x0;
  PINMODE4_bit.P2_12 = 0;
  FIO2DIR_bit.P2_12 = 1;
  FIO2MASK_bit.P2_12 = 0;
  PLAYER_XDCS_SET();

  // (XCS) модуля
  PINSEL4_bit.P2_5 = 0x0;
  PINMODE4_bit.P2_5 = 0;
  FIO2DIR_bit.P2_5 = 1;
  FIO2MASK_bit.P2_5 = 0;
  PLAYER_XCS_SET();

  // DREQ (вход контроллера)  
  PINSEL3_bit.P1_31 = 0x0;
  PINMODE3_bit.P1_31 = 0;
  FIO1DIR_bit.P1_31 = 0;
  FIO1MASK_bit.P1_31 = 0;

  OSTimeDly(1);
  PLAYER_XRESET_CLR();
  OSTimeDly(20);
  PLAYER_XRESET_SET();
  OSTimeDly(50);

  return 0;
}



/* Note: code SS_VER=2 is used for both VS1002 and VS1011e */
const u_int16 chipNumber[16] = {
  1001, 1011, 1011, 1003, 1053, 1033, 1063, 1103,
  0, 0, 0, 0, 0, 0, 0, 0
};

/*

  Software Initialization for VS1053.

  Note that you need to check whether SM_SDISHARE should be set in
  your application or not.
  
*/
int player_init(void) {
  u_int16 ssVer;

  player_conn = 0;

  playerInitHardware();

  spi1_setSpeed(100000);
  
  /* Start initialization with a dummy read, which makes sure our
     microcontoller chips selects and everything are where they
     are supposed to be and that VS10xx's SCI bus is in a known state. */
  ReadSci(SCI_MODE);

  /* First real operation is a software reset. After the software
     reset we know what the status of the IC is. You need, depending
     on your application, either set or not set SM_SDISHARE. See the
     Datasheet for details. */
  WriteSci(SCI_MODE, SM_SDINEW|SM_RESET);
  OSTimeDly(10);
  /*
  int retry = 0;
  while (ReadSci(SCI_MODE) != SM_SDINEW) 
  {
     if (retry++ > 10)
     {
        return -8;
     }
      WriteSci(SCI_MODE, SM_SDINEW|SM_RESET);
      OSTimeDly(10);
  }
  */
  /* A quick sanity check: write to two registers, then test if we
     get the same results. Note that if you use a too high SPI
     speed, the MSB is the most likely to fail when read again. */
  WriteSci(SCI_AICTRL1, 0xABAD);
  WriteSci(SCI_AICTRL2, 0x7E57);
  if (ReadSci(SCI_AICTRL1) != 0xABAD || ReadSci(SCI_AICTRL2) != 0x7E57) {
    return -1;
  }
  WriteSci(SCI_AICTRL1, 0);
  WriteSci(SCI_AICTRL2, 0);

  /* Check VS10xx type */
  ssVer = ((ReadSci(SCI_STATUS) >> 4) & 15);
  if (chipNumber[ssVer]) {
    if (chipNumber[ssVer] != 1053) {
      return -2;
    }
  } else {
    return -3;
  }

  /* Set the clock. Until this point we need to run SPI slow so that
     we do not exceed the maximum speeds mentioned in
     Chapter SPI Timing Diagram in the Datasheet. */
  WriteSci(SCI_CLOCKF,
           HZ_TO_SC_FREQ(12288000) | SC_MULT_53_35X | SC_ADD_53_10X);


  /* Now when we have upped the VS10xx clock speed, the microcontroller
     SPI bus can run faster. Do that before you start playing or
     recording files. */

  OSTimeDly(10);
  
  spi1_setSpeed(10000000);
          
  /* Set up other parameters. */
  WriteVS10xxMem(PAR_CONFIG1, PAR_CONFIG1_AAC_SBR_SELECTIVE_UPSAMPLE);

  /* Set volume level at -6 dB of maximum */
  WriteSci(SCI_VOL, 0x0c0c);

  /* We're ready to go. */
  player_conn = 1;
  return 0;
}

int player_connected(void)
{
    return player_conn;
}
