peddamat
5/21/2013 - 7:04 PM

codec.c

/******************************************************************************
 * Pixie Firmware
 *                      (c) Bafoontecha.  All rights reserved.
 *
 * File Name     : codec.c
 * Description   : Firmware for TLC320AIC23 codec.
 *
 * Version : 0.1
 *
 *****************************************************************************/

#include "codec.h"
#include "i2c_wrap.h"
#include <string.h>

extern void process_dci_interrupt(void);

/* Let the outside world know the buffers need fill'in */
extern char refillBufferA, refillBufferB;

/******************************************************************************
 * DMA Interrupts - These interrupts are called when the DMA-module is enabled
 *                  AND the DMA buffers are filled.  These routines should
 *                  return as quickly as possible.
 *****************************************************************************/

void __attribute__((interrupt, no_auto_psv)) _DMA0Interrupt(void)
{
  static unsigned int TxBufferCount = 0;

  if (TxBufferCount == 0)
    refillBufferA = 1;
  else
    refillBufferB = 1;

  TxBufferCount ^= 1;
  IFS0bits.DMA0IF = 0;
}

void __attribute__((interrupt, no_auto_psv)) _DMA1Interrupt(void)
{
  static unsigned int RxBufferCount = 0;

  if (RxBufferCount == 0)
    ;//refillBufferA = 1;
  else
    ;//refillBufferB = 1;

  RxBufferCount ^= 1;
  IFS0bits.DMA1IF = 0;
}

/******************************************************************************
 * DCI Interrupts - This is the alternate interrupt routine.  It's called when
 *                  the codec is configured to operate in DCI mode.
 *****************************************************************************/

void __attribute__((__interrupt__, no_auto_psv)) _DCIInterrupt(void)
{
  process_dci_interrupt();

  IFS3bits.DCIIF = 0;
}

/******************************************************************************
 * DMA Interface Setup Routines - __builtin_dmaoffset ()
 *****************************************************************************/

void setup_dma(unsigned int txa, unsigned int txb, unsigned int rxa, unsigned int rxb)
{
  // Setup DMA Channel 0 for Transmit in Continuous Ping-Pong Mode
  DMA0CONbits.SIZE = 0;   // Data size = word
  DMA0CONbits.DIR = 1;    // Read from DSPRAM to Peripheral
  DMA0CONbits.HALF = 0;   // Interrupt when all data moved
  DMA0CONbits.NULLW = 0;  // Normal data writes
  DMA0CONbits.AMODE = 0;  // Register Indirect w/Post-Increment Mode
  DMA0CONbits.MODE = 2;   // Continuous Ping-Pong

  DMA0REQbits.FORCE = 0;
  DMA0REQbits.IRQSEL = 0x3C;

  DMA0CNT = CODEC_BUFFER_SIZE - 1;

  DMA0PAD = (volatile unsigned int) &TXBUF0;
  DMA0STA = txa;
  DMA0STB = txb;

  IFS0bits.DMA0IF = 0;
  IEC0bits.DMA0IE = 1;

  // Setup DMA Channel 1 for Receive in Continuous Ping-Pong Mode
  DMA1CONbits.SIZE = 0;   // Data size = word
  DMA1CONbits.DIR = 0;    // Read from DSPRAM to Peripheral
  DMA1CONbits.HALF = 0;   // Interrupt when all data moved
  DMA1CONbits.NULLW = 0;  // Normal data writes
  DMA1CONbits.AMODE = 0;  // Register Indirect w/Post-Increment Mode
  DMA1CONbits.MODE = 2;   // Continuous Ping-Pong

  DMA1REQbits.FORCE = 0;
  DMA1REQbits.IRQSEL = 0x3C;

  DMA1CNT = CODEC_BUFFER_SIZE - 1;

  DMA1PAD = (volatile unsigned int) &RXBUF0;
  DMA1STA = rxa;
  DMA1STB = rxb;

  IFS0bits.DMA1IF = 0;
  IEC0bits.DMA1IE = 1;
}

/******************************************************************************
 * DCI Interface Setup Routines
 ******************************************************************************/

void setup_dci(void)
{
  DCICON1bits.DCIEN = 0;      // Disable module before configuring

  RSCONbits.RSE0 = 1;
  RSCONbits.RSE1 = 0;

  TSCONbits.TSE0 = 1;
  TSCONbits.TSE1 = 1;

  DCICON1bits.DCISIDL = 1;    // Module halts in CPU Idle mode
  DCICON1bits.CSCKD = 1;      // SCK Input
  DCICON1bits.CSCKE = 1;      // Data changes on clock falling, sampled on rising
  DCICON1bits.COFSD = 1;      // FS Input
  DCICON1bits.CSDOM = 0;
  DCICON1bits.DJST = 0;       // Wait 1 cycle before transmit
  DCICON1bits.COFSM = 0;      // I2S Mode
  DCICON2bits.WS = 15;        // 16-bit words

  DCICON2bits.COFSG = CODEC_DCI_FRAME_NUM;
  DCICON2bits.BLEN = CODEC_DCI_BUFFER_LENGTH;

  DCICON3 = 0;

  IPC15bits.DCIIP = 6;
  IFS3bits.DCIIF = 0;
  IEC3bits.DCIIE = 0;

  TXBUF0 = 0;
  TXBUF1 = 0;
}

/******************************************************************************
 *
 ******************************************************************************/

void setup_codec(unsigned int txa, unsigned int txb,
                 unsigned int rxa, unsigned int rxb)
{
  // First, ready the dsPIC
  if (CODEC_CONFIG == DCI)
  {
    setup_dci();
  }
  else if (CODEC_CONFIG == DMA)
  {
    setup_dci();
    setup_dma(txa, txb, rxa, rxb);
  }

  // Next, configure the codec
  codec_reset();
  codec_set_input (CODEC_DEFAULT_INPUT);
  codec_set_invol (CODEC_DEFAULT_INPUT_VOLUME);
  codec_set_outvol(CODEC_DEFAULT_OUTPUT_VOLUME);
  codec_set_samplingrate(CODEC_DEFAULT_SAMPLING_RATE);
  codec_finish_setup();

  // Fire up the interface!
  if (CODEC_CONFIG == DMA)
  {
    DMA0REQbits.FORCE = 1;
    while (DMA0REQbits.FORCE == 1);

    DMA0REQbits.FORCE = 1;
    while (DMA0REQbits.FORCE == 1);

    /* Fire up the DMAs */
    DMA0CONbits.CHEN = 1;
    DMA1CONbits.CHEN = 1;
  }
  else if (CODEC_CONFIG == DCI)
  {
    _DCIIE = 1;
  }

  DCICON1bits.DCIEN = 1;             /* Enable the DCI module */
}

inline void codec_start(void)
{
  DCICON1bits.DCIEN = 1;             /* Start the DCI module  */
}

inline void codec_pause(void)
{
  DCICON1bits.DCIEN = 0;             /* Stop the DCI module */
}

inline void codec_reset(void)
{
  int commandValue, result;

  commandValue = 0;   /* Any value can be written to reset the codec  */
  result = codec_ctrl(TLV320_RESET, (void *) &commandValue);
  if (result == -1) while (1);
}

inline void codec_set_invol(unsigned char volume)
{
  int commandValue, result;

  volume &= 0x1f;

  commandValue = 0b100011111; 
  codec_ctrl(TLV320_VOL_LLINE, (void *) &commandValue);
  if (result == -1) while (1);
}

inline void codec_set_outvol(unsigned char volume)
{
  int commandValue, result;

  volume &= 0x7f;

  commandValue = VOL_LHEAD_LRS |
                 VOL_LHEAD_LZC |
                 volume;
  codec_ctrl(TLV320_VOL_LHEAD, (void *) &commandValue);
  if (result == -1) while (1);
}

inline void codec_set_input(INPUT_SELECT input)
{
  int commandValue, result;

  if (input == LINE_IN)
    commandValue = 0b000010000;
  else if (input == MIC_IN)
    commandValue = 0b000010101;
  else
    while (1);

  codec_ctrl(TLV320_ANLG_CTRL, (void *) &commandValue);
  if (result == -1) while (1);
}

inline void codec_set_samplingrate(SAMPLING_RATE sampling_rate)
{
  int commandValue, result;

  if (sampling_rate == SR_8k)
    commandValue = 0b000001111;  // 8k
  else if (sampling_rate == SR_44k)
    commandValue = 0b000100011; // 44.1k
  else
    while (1);

  codec_ctrl(TLV320_SRATE_CTRL, (void *) &commandValue);
  if (result == -1) while (1);
}

// Checked correct!
inline void codec_finish_setup()
{
  int commandValue, result;

  commandValue = 0b000000100;
  codec_ctrl(TLV320_DGTL_CTRL, (void *) &commandValue);
  if (result == -1) while (1);

  commandValue = 0b000000000;     // All peripherals on
  codec_ctrl(TLV320_PWR_CTRL, (void *) &commandValue);
  if (result == -1) while (1);

  commandValue = 0b001010011;     // Master, 16-bit, DSP format
  codec_ctrl(TLV320_DGTL_FMT, (void *) &commandValue);
  if (result == -1) while (1);

  commandValue = 0b000000001;
  codec_ctrl(TLV320_DGTL_IF_ACT, (void *) &commandValue);
  if (result == -1) while (1);
}

// Checked correct!
int codec_ctrl(int command, void *value)
{
  /* Use the I2C module to send the control data to the codec
   * Send the device address first, the upper control word next
   * which consists of register address and bit B8 of control data
   * and finally send the control data bits B7-B0 */

  CommandValueWord commandValue;

  commandValue.wordValue = *((int *)value);
  commandValue.bytes[1] &= 0x1;
  command = command << 1;
  command = command | commandValue.bytes[1];

  i2c_start(TLV320DRV_I2C_ADDR);
  i2c_command(command);
  i2c_command(commandValue.bytes[0]);
  i2c_stop();
}