/*****************************************************************************\

  File Name: AudDriverFuncs_AD1836.c

  Description: This module contains sources for the interface functions to the
               AD1836 audio codec driver.

  Date Modified:
    050623 - TL: Original source code
    060407 - GS: Modified it so that it uses the new 1836A mach ii driver

\*****************************************************************************/

//-----------------------------------------------------------------------------
//  I N C L U D E S
//-----------------------------------------------------------------------------
#include "AudDriverFuncs.h"
#include <drivers/codec/adi_ad1836a_ii.h>

//-----------------------------------------------------------------------------
//  E X T E R N S    G L O B A L S    &    F U N C T I O N S
//-----------------------------------------------------------------------------
extern void Reset_AD1836(void);

//-----------------------------------------------------------------------------
//  D E F I N E S    &    M A C R O S
//-----------------------------------------------------------------------------
#define LOOPONERRORCHK(x) ezErrorCheck(x)

//Do these only if DEBUG is enabled!
#if defined (SND_DEV_DEBUG)
#define DBG_INCREMENT(x)      x++
#define DBG_DECREMENT(x)      x--
#define DBG_SGNALERROR()      LOOPONERRORCHK(~0)
#define DBG_LOOPONERRORCHK(x) LOOPONERRORCHK(x)
#else
#define DBG_INCREMENT(x)
#define DBG_DECREMENT(x)
#define DBG_SGNALERROR()
#define DBG_LOOPONERRORCHK(x)
#endif

//-----------------------------------------------------------------------------
//  G L O B A L S
//-----------------------------------------------------------------------------
#ifndef AUDIO_SUPPORT_INPUT_ONLY
  static ADI_DEV_1D_BUFFER SPORTOut[2];
#endif

#ifndef AUDIO_SUPPORT_OUTPUT_ONLY
  static ADI_DEV_1D_BUFFER SPORTIn[2];
#endif

static ADI_DEV_DEVICE_HANDLE ADIAD1836DeviceHandle;

#if defined (SND_DEV_DEBUG)
volatile int SPORTCounter = 0;
volatile int SPORTPingCounter = 0;
volatile int SPORTPongCounter = 0;
#endif

#ifdef AUDIO_SUPPORT_INTPUT_ONLY
#define SUPPORTED_NUM_OUT_CHANNELS (0)
#else
#define SUPPORTED_NUM_OUT_CHANNELS (1<<2)
#endif

#ifdef AUDIO_SUPPORT_OUTPUT_ONLY
#define SUPPORTED_NUM_IN_CHANNELS (0)
#else
#define SUPPORTED_NUM_IN_CHANNELS (1<<2)
#endif

AUDIOSTATE *g_st;

#if   defined (__ADSPBF533__)
// addresses for Port B in Flash A (used for resetting the codec)
#define pFlashA_PortA_Dir  (volatile unsigned char *)0x20270006
#define pFlashA_PortA_Data (volatile unsigned char *)0x20270004
#define SLAVESELECT 4  //SPI Slave Select connected to 1836
#elif defined (__ADSPBF537__)
#elif defined (__ADSPBF561__)
#define SLAVESELECT 4  //SPI Slave Select connected to 1836
#endif

//-----------------------------------------------------------------------------
//  C O N S T S    &    T A B L E S
//-----------------------------------------------------------------------------
const u32 supportedSampleRates[] = {48000, 24000, 16000};
AUDDEVPROPS AudDevIinitProps = {
                                 SUPPORTED_NUM_IN_CHANNELS,
                                 SUPPORTED_NUM_OUT_CHANNELS,
                                 sizeof(supportedSampleRates),
                                 supportedSampleRates,
                                 AUD_SAMPLFORMAT_FIXED_ALL };
#define PAUDDEVINITPROPERTIES &AudDevIinitProps

ADI_DEV_CMD_VALUE_PAIR ADIAD1836ConfigurationTable [] =
{
  {ADI_AD1836A_CMD_SET_SPORT_STATUS, (void *)ADI_AD1836A_SPORT_OPEN},
  {ADI_DEV_CMD_SET_DATAFLOW_METHOD,  (void *)ADI_DEV_MODE_CHAINED_LOOPBACK},
  {ADI_DEV_CMD_SET_STREAMING,        (void *)TRUE},
  {ADI_AD1836A_CMD_SET_SPI_CS,       (void *)SLAVESELECT},
  {ADI_AD1836A_CMD_SET_SPORT_OPERATION_MODE, (void *)ADI_AD1836A_SET_I2S_MODE},
  {ADI_DEV_CMD_END,NULL}
};

ADI_DEV_ACCESS_REGISTER gConfig1836A[] =
{
    {AD1836A_DAC_CTRL_1, 0x0000},
    {AD1836A_DAC_CTRL_2, 0x0000},
    {AD1836A_DAC_1L_VOL, 0x03FF},
    {AD1836A_DAC_1R_VOL, 0x03FF},
  {AD1836A_DAC_2L_VOL, 0x03FF},
  {AD1836A_DAC_2R_VOL, 0x03FF},
  {AD1836A_DAC_3L_VOL, 0x03FF},
  {AD1836A_DAC_3R_VOL, 0x03FF},
  {AD1836A_ADC_CTRL_1, 0x0000},
  {AD1836A_ADC_CTRL_2, 0x0000},
  {AD1836A_ADC_CTRL_3, 0x0080},
  {ADI_DEV_REGEND,     0x0000}
};
#define POS_ADCCTRL3   10 //It's position in the "gConfig1836A[]" array is it?

//-----------------------------------------------------------------------------
//  F U N C T I O N    P R O T O T Y P E S
//-----------------------------------------------------------------------------
void          Reset_AD1836(void);
static void   SPORTCallback(void *, u32, void *);
AUDDEVRESULTS AudioDriver_Query(PAUDDEVPROPS *);
AUDDEVRESULTS AudioDriver_Open(PAUDIOSTATE);
AUDDEVRESULTS AudioDriver_Start(void);
AUDDEVRESULTS AudioDriver_Stop(void);
AUDDEVRESULTS AudioDriver_Close(void);

//-----------------------------------------------------------------------------
// Reset_AD1836() - Calls reset function in ezkitutilities
//

//
// NOTE:  This is BlackFin processor specific.
//-----------------------------------------------------------------------------
void Reset_AD1836(void)
{
  ezReset1836();
}

//-----------------------------------------------------------------------------
//  SPORTCallback() - Gets called by the SPORT device driver when a block of
//                    data has been transferred.
//-----------------------------------------------------------------------------
static void SPORTCallback(void *AppHandle, u32 Event, void *pArg)
{
  u8  *Tx=NULL, *Rx=NULL;
  int index = *((int *)pArg);
  int Result;

  DBG_INCREMENT(SPORTCounter);

  if ((Event == ADI_DEV_EVENT_DMA_ERROR_INTERRUPT) ||
      (Event == ADI_SPORT_EVENT_ERROR_INTERRUPT))
    { ezErrorCheck(Event); }

  if ((g_st->Direction == ADI_DEV_DIRECTION_INBOUND) ||
      (g_st->Direction == ADI_DEV_DIRECTION_BIDIRECTIONAL))
  {
    if (pArg == g_st->RxBuffer)
    { //"ping" -> Device Driver finished on the 1st half of the double buffer
      DBG_INCREMENT(SPORTCounter);
      Rx = g_st->RxBuffer;
      if (g_st->Direction == ADI_DEV_DIRECTION_BIDIRECTIONAL)
        { Tx = g_st->TxBuffer; }
    }
    else if (pArg == g_st->RxBuffer      + g_st->NumFrames *
                     g_st->NumInChannels * g_st->SampleHostSize)
    { //"pong" -> Device Driver finished on the 2nd half of the double buffer
      DBG_INCREMENT(SPORTCounter);
      Rx = g_st->RxBuffer      + g_st->NumFrames *
           g_st->NumInChannels * g_st->SampleHostSize;
      if (g_st->Direction == ADI_DEV_DIRECTION_BIDIRECTIONAL)
      {
        Tx = g_st->TxBuffer       + g_st->NumFrames *
             g_st->NumOutChannels * g_st->SampleHostSize;
      }
    }
    else { DBG_SGNALERROR(); } //Error occured!
  }
  else
  { // st->Direction == SND_DIRECTION_OUTBOUND
    if (pArg == g_st->TxBuffer)
    { // "ping" -> Device Driver finished on the 1st half of the double buffer
      DBG_INCREMENT(SPORTCounter);
      Tx = g_st->TxBuffer;
    }
    else if (pArg == g_st->TxBuffer       + g_st->NumFrames *
                     g_st->NumOutChannels * g_st->SampleHostSize)
    { // "pong" -> Device Driver finished on the 2nd half of the double buffer
      DBG_INCREMENT(SPORTCounter);
      Tx = g_st->TxBuffer       + g_st->NumFrames *
           g_st->NumOutChannels * g_st->SampleHostSize;
    }
    else { DBG_SGNALERROR(); } //Error occured!
  }

  // Finally call the application
  Result = g_st->pCBFunc((PVOID)Rx, (PVOID)Tx, NULL);
 if (Result != 0) { AudDev_Stop(); } // Quit if "callback" return is !success
}

//-----------------------------------------------------------------------------
// AudioDriver_Query() - Initializes the sound driver with some Sytem Services
//                       managers, and resets the ADC/DAC
//-----------------------------------------------------------------------------
AUDDEVRESULTS AudioDriver_Query(PAUDDEVPROPS *ppDevProp)
{
  *ppDevProp = PAUDDEVINITPROPERTIES; //Reset the ADC and DAC
  return 0;
}

//-----------------------------------------------------------------------------
// AudioDriver_Open() - Opens an audio stream
//-----------------------------------------------------------------------------
AUDDEVRESULTS AudioDriver_Open(PAUDIOSTATE pAS)
{
  ADI_DEV_CMD_VALUE_PAIR *pCommandPair;
  int                    i, Result, adc3;

  g_st = pAS;

  // Open SPORT0 for communication
  Reset_AD1836(); //First hard reset the AD1836 codec
  if (ADI_DEV_DIRECTION_UNDEFINED == g_st->Direction)
    { return AUD_ERR_DEVICEOPEN; }

  if (AUD_SUCCESS != adi_dev_Open(g_st->DeviceManagerHandle,
                                  &ADIAD1836AEntryPoint,
                                  0, NULL, &ADIAD1836DeviceHandle,
                                  g_st->Direction,g_st->DMAHandle,
                                  g_st->DCBHandle, SPORTCallback))
    { return AUD_ERR_DEVICEOPEN; }

  // Configure SPORT0 using the table defined above
  //for (i = 0, pCommandPair = ADIAD1836ConfigurationTable;
   //    i < (sizeof(ADIAD1836ConfigurationTable)/sizeof(ADI_DEV_CMD_VALUE_PAIR));
    //   i++, pCommandPair++)
  //{
   // if ((Result = adi_dev_Control(ADIAD1836DeviceHandle,
   //                               pCommandPair->CommandID,
    //                              pCommandPair->Value)) != ADI_DEV_RESULT_SUCCESS)
   //   { break; }
  //}
   Result = adi_dev_Control(ADIAD1836DeviceHandle,ADI_DEV_CMD_TABLE,ADIAD1836ConfigurationTable);

  ezErrorCheck(Result);

  //Configure CODEC registers, but first set up ADC3 value...
  if (g_st->SampleRate == 48000) { adc3 = 0x000; }
  else if (g_st->SampleRate == 24000) { adc3 = 0x040; }
  else if (g_st->SampleRate == 16000) { adc3 = 0x080; }
  (gConfig1836A[POS_ADCCTRL3]).Data = (u16)adc3;
  Result = adi_dev_Control(ADIAD1836DeviceHandle,
                           ADI_DEV_CMD_REGISTER_TABLE_WRITE,
                           (void *)gConfig1836A); //Set up all registers
  ezErrorCheck(Result);

  // Set up the SPORT device driver buffers
  if ((g_st->Direction == ADI_DEV_DIRECTION_OUTBOUND) ||
      (g_st->Direction == ADI_DEV_DIRECTION_BIDIRECTIONAL))
  {
    //Set up the device outbound buffer
    SPORTOut[0].Data         = g_st->TxBuffer;
    SPORTOut[0].ElementWidth = g_st->SampleHostSize;
    SPORTOut[0].ElementCount = g_st->NumFrames * g_st->NumOutChannels;
    if (g_st->Direction == ADI_DEV_DIRECTION_OUTBOUND)
      { SPORTOut[0].CallbackParameter = g_st->TxBuffer; }
    else { SPORTOut[0].CallbackParameter = NULL; }
    SPORTOut[0].pNext = &SPORTOut[1];

    //Set up the device outbound buffer
    SPORTOut[1].Data = g_st->TxBuffer       + g_st->NumFrames *
                       g_st->NumOutChannels * g_st->SampleHostSize;
    SPORTOut[1].ElementWidth = g_st->SampleHostSize;
    SPORTOut[1].ElementCount = g_st->NumFrames * g_st->NumOutChannels;
    if (g_st->Direction == ADI_DEV_DIRECTION_OUTBOUND)
    {
      SPORTOut[1].CallbackParameter = g_st->TxBuffer       +
                                      g_st->NumFrames      *
                                      g_st->NumOutChannels *
                                      g_st->SampleHostSize;
    }
    else { SPORTOut[1].CallbackParameter = NULL; }
    SPORTOut[1].pNext = NULL;
    Result = adi_dev_Control(ADIAD1836DeviceHandle,
                             ADI_SPORT_CMD_SET_TX_WORD_LENGTH,
                             (void *)(g_st->SampleSize-1));

    //Submit the output buffer to the device manager
    Result = adi_dev_Write(ADIAD1836DeviceHandle, ADI_DEV_1D,
                           (ADI_DEV_BUFFER *)&SPORTOut[0]);
    ezErrorCheck(Result);
  }

  if ((g_st->Direction == ADI_DEV_DIRECTION_INBOUND) ||
      (g_st->Direction == ADI_DEV_DIRECTION_BIDIRECTIONAL))
  {
    // Set up the device inbound buffer
    SPORTIn[0].Data              = g_st->RxBuffer;
    SPORTIn[0].ElementWidth      = g_st->SampleHostSize;
    SPORTIn[0].ElementCount      = g_st->NumFrames * g_st->NumInChannels;
    SPORTIn[0].CallbackParameter = g_st->RxBuffer;
    SPORTIn[0].pNext             = &SPORTIn[1];

    // Set up the device inbound buffer
    SPORTIn[1].Data              = g_st->RxBuffer      + g_st->NumFrames *
                                   g_st->NumInChannels * g_st->SampleHostSize;
    SPORTIn[1].ElementWidth      = g_st->SampleHostSize;
    SPORTIn[1].ElementCount      = g_st->NumFrames * g_st->NumInChannels;
    SPORTIn[1].CallbackParameter = g_st->RxBuffer       +  g_st->NumFrames *
                                   g_st->NumOutChannels *  g_st->SampleHostSize;
    SPORTIn[1].pNext = NULL;

    Result = adi_dev_Control(ADIAD1836DeviceHandle,
                             ADI_SPORT_CMD_SET_RX_WORD_LENGTH,
                             (PVOID)(g_st->SampleSize-1));

    // Submit the output buffer to the device manager
    Result = adi_dev_Read(ADIAD1836DeviceHandle,
                          ADI_DEV_1D,
                          (ADI_DEV_BUFFER *)&SPORTIn[0]);
    ezErrorCheck(Result);
  }
  return 0;
}

//-----------------------------------------------------------------------------
// AudioDriver_Start() - Starts the audio driver and stream
//-----------------------------------------------------------------------------
AUDDEVRESULTS AudioDriver_Start(void)
{
  int Result;

  Result = adi_dev_Control(ADIAD1836DeviceHandle, ADI_DEV_CMD_SET_DATAFLOW,
                           (void *)TRUE); //Enable data flow
  ezErrorCheck(Result);
  return 0;
}

//-----------------------------------------------------------------------------
// AudioDriver_Stop() - Stops the audio driver and stream
//-----------------------------------------------------------------------------
AUDDEVRESULTS AudioDriver_Stop(void)
{
  int Result;

  Result = adi_dev_Control(ADIAD1836DeviceHandle, ADI_DEV_CMD_SET_DATAFLOW,
                           (void *)FALSE); //Enable data flow
  ezErrorCheck(Result);
  return AUD_SUCCESS;
}

//-----------------------------------------------------------------------------
// AudioDriver_Close() - Closes the sound driver and the stream
//-----------------------------------------------------------------------------
AUDDEVRESULTS AudioDriver_Close(void)
{
  int Result;

  Result = adi_dev_Close(ADIAD1836DeviceHandle); // Close SPORT0
  ezErrorCheck(Result);
  return AUD_SUCCESS;
}

