/******************************************************************************
* 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();
}