/* ---------------------------------------------------------------------------- | |
* ATMEL Microcontroller Software Support | |
* ---------------------------------------------------------------------------- | |
* Copyright (c) 2008, Atmel Corporation | |
* | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* - Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the disclaimer below. | |
* | |
* Atmel's name may not be used to endorse or promote products derived from | |
* this software without specific prior written permission. | |
* | |
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE | |
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* ---------------------------------------------------------------------------- | |
*/ | |
//------------------------------------------------------------------------------ | |
// Headers | |
//------------------------------------------------------------------------------ | |
#include "ac97c.h" | |
#include <board.h> | |
#include <aic/aic.h> | |
#include <utility/assert.h> | |
#include <utility/trace.h> | |
#include <utility/math.h> | |
//------------------------------------------------------------------------------ | |
// Local constants | |
//------------------------------------------------------------------------------ | |
/// Maximum size of one PDC buffer (in bytes). | |
#define MAX_PDC_COUNTER 65535 | |
//------------------------------------------------------------------------------ | |
// Local types | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
/// AC97 transfer descriptor. Tracks the status and parameters of a transfer | |
/// on the AC97 bus. | |
//------------------------------------------------------------------------------ | |
typedef struct _Ac97Transfer { | |
/// Buffer containing the slots to send. | |
unsigned char *pBuffer; | |
/// Total number of samples to send. | |
volatile unsigned int numSamples; | |
/// Optional callback function. | |
Ac97Callback callback; | |
/// Optional argument to the callback function. | |
void *pArg; | |
} Ac97Transfer; | |
//------------------------------------------------------------------------------ | |
/// AC97 controller driver structure. Monitors the status of transfers on all | |
/// AC97 channels. | |
//------------------------------------------------------------------------------ | |
typedef struct _Ac97c { | |
/// List of transfers occuring on each channel. | |
Ac97Transfer transfers[5]; | |
} Ac97c; | |
//------------------------------------------------------------------------------ | |
// Local variables | |
//------------------------------------------------------------------------------ | |
/// Global AC97 controller instance. | |
static Ac97c ac97c; | |
//------------------------------------------------------------------------------ | |
// Local functions | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
/// Returns the size of one sample (in bytes) on the given channel. | |
/// \param channel Channel number. | |
//------------------------------------------------------------------------------ | |
static unsigned char GetSampleSize(unsigned char channel) | |
{ | |
unsigned int size = 0; | |
SANITY_CHECK((channel == AC97C_CHANNEL_A) | |
|| (channel == AC97C_CHANNEL_B) | |
|| (channel == AC97C_CHANNEL_CODEC)); | |
// Check selected channel | |
switch (channel) { | |
case AC97C_CHANNEL_CODEC: return 2; | |
case AC97C_CHANNEL_A: | |
size = (AT91C_BASE_AC97C->AC97C_CAMR & AT91C_AC97C_SIZE) >> 16; | |
break; | |
case AC97C_CHANNEL_B: | |
size = (AT91C_BASE_AC97C->AC97C_CBMR & AT91C_AC97C_SIZE) >> 16; | |
break; | |
} | |
// Compute size in bytes given SIZE field | |
if ((size & 2) != 0) { | |
return 2; | |
} | |
else { | |
return 4; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Interrupt service routine for Codec, is invoked by AC97C_Handler. | |
//------------------------------------------------------------------------------ | |
static void CodecHandler(void) | |
{ | |
unsigned int status; | |
unsigned int data; | |
Ac97Transfer *pTransfer = &(ac97c.transfers[AC97C_CODEC_TRANSFER]); | |
// Read CODEC status register | |
status = AT91C_BASE_AC97C->AC97C_COSR; | |
status &= AT91C_BASE_AC97C->AC97C_COMR; | |
// A sample has been transmitted | |
if (status & AT91C_AC97C_TXRDY) { | |
pTransfer->numSamples--; | |
// If there are remaining samples, transmit one | |
if (pTransfer->numSamples > 0) { | |
data = *((unsigned int *) pTransfer->pBuffer); | |
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_TXRDY); | |
AT91C_BASE_AC97C->AC97C_COTHR = data; | |
// Check if transfer is read or write | |
if ((data & AT91C_AC97C_READ) != 0) { | |
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY; | |
} | |
else { | |
pTransfer->pBuffer += sizeof(unsigned int); | |
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY; | |
} | |
} | |
// Transfer finished | |
else { | |
AT91C_BASE_AC97C->AC97C_IDR = AT91C_AC97C_COEVT; | |
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_TXRDY); | |
if (pTransfer->callback) { | |
pTransfer->callback(pTransfer->pArg, 0, 0); | |
} | |
} | |
} | |
// A sample has been received | |
if (status & AT91C_AC97C_RXRDY) { | |
// Store sample | |
data = AT91C_BASE_AC97C->AC97C_CORHR; | |
*((unsigned int *) pTransfer->pBuffer) = data; | |
pTransfer->pBuffer += sizeof(unsigned int); | |
pTransfer->numSamples--; | |
// Transfer finished | |
if (pTransfer->numSamples > 0) { | |
data = *((unsigned int *) pTransfer->pBuffer); | |
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_RXRDY); | |
AT91C_BASE_AC97C->AC97C_COTHR = data; | |
// Check if transfer is read or write | |
if ((data & AT91C_AC97C_READ) != 0) { | |
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY; | |
} | |
else { | |
pTransfer->pBuffer += sizeof(unsigned int); | |
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY; | |
} | |
} | |
else { | |
AT91C_BASE_AC97C->AC97C_IDR = AT91C_AC97C_COEVT; | |
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_RXRDY); | |
if (pTransfer->callback) { | |
pTransfer->callback(pTransfer->pArg, 0, 0); | |
} | |
} | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Interrupt service routine for channel A, is invoked by AC97C_Handler. | |
//------------------------------------------------------------------------------ | |
static void ChannelAHandler(void) | |
{ | |
unsigned int status; | |
Ac97Transfer *pTransmit = &(ac97c.transfers[AC97C_CHANNEL_A_TRANSMIT]); | |
Ac97Transfer *pReceive = &(ac97c.transfers[AC97C_CHANNEL_A_RECEIVE]); | |
// Read channel A status register | |
status = AT91C_BASE_AC97C->AC97C_CASR; | |
// A buffer has been transmitted | |
if ((status & AT91C_AC97C_ENDTX) != 0) { | |
// Update transfer information | |
if (pTransmit->numSamples > MAX_PDC_COUNTER) { | |
pTransmit->numSamples -= MAX_PDC_COUNTER; | |
} | |
else { | |
pTransmit->numSamples = 0; | |
} | |
// Transmit new buffers if necessary | |
if (pTransmit->numSamples > MAX_PDC_COUNTER) { | |
// Fill next PDC | |
AT91C_BASE_AC97C->AC97C_TNPR = (unsigned int) pTransmit->pBuffer; | |
if (pTransmit->numSamples > 2 * MAX_PDC_COUNTER) { | |
AT91C_BASE_AC97C->AC97C_TNCR = MAX_PDC_COUNTER; | |
pTransmit->pBuffer += MAX_PDC_COUNTER | |
* GetSampleSize(AC97C_CHANNEL_A); | |
} | |
else { | |
AT91C_BASE_AC97C->AC97C_TNCR = pTransmit->numSamples | |
- MAX_PDC_COUNTER; | |
} | |
} | |
// Only one buffer remaining | |
else { | |
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_ENDTX; | |
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_TXBUFE; | |
} | |
} | |
// Transmit completed | |
if ((status & AT91C_AC97C_TXBUFE) != 0) { | |
pTransmit->numSamples = 0; | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS; | |
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_TXBUFE; | |
if (pTransmit->callback) { | |
pTransmit->callback(pTransmit->pArg, 0, 0); | |
} | |
} | |
// A buffer has been received | |
if (status & AT91C_AC97C_ENDRX) { | |
if (pReceive->numSamples > MAX_PDC_COUNTER) { | |
pReceive->numSamples -= MAX_PDC_COUNTER; | |
} | |
else { | |
pReceive->numSamples = 0; | |
} | |
// Transfer remaining samples | |
if (pReceive->numSamples > MAX_PDC_COUNTER) { | |
AT91C_BASE_AC97C->AC97C_RNPR = (unsigned int) pReceive->pBuffer; | |
if (pReceive->numSamples > 2 * MAX_PDC_COUNTER) { | |
AT91C_BASE_AC97C->AC97C_RNCR = MAX_PDC_COUNTER; | |
pReceive->pBuffer += MAX_PDC_COUNTER | |
* GetSampleSize(AC97C_CHANNEL_A); | |
} | |
else { | |
AT91C_BASE_AC97C->AC97C_RNCR = pReceive->numSamples | |
- MAX_PDC_COUNTER; | |
} | |
} | |
// Only one buffer remaining | |
else { | |
AT91C_BASE_AC97C->AC97C_CAMR &= ~(AT91C_AC97C_ENDRX); | |
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_RXBUFF; | |
} | |
} | |
// Receive complete | |
if ((status & AT91C_AC97C_RXBUFF) != 0) { | |
pReceive->numSamples = 0; | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS; | |
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_RXBUFF; | |
if (pReceive->callback) { | |
pReceive->callback(pReceive->pArg, 0, 0); | |
} | |
} | |
} | |
//------------------------------------------------------------------------------ | |
// Exported functions | |
//------------------------------------------------------------------------------ | |
//------------------------------------------------------------------------------ | |
/// This handler function must be called by the AC97C interrupt service routine. | |
/// Identifies which event was activated and calls the associated function. | |
//------------------------------------------------------------------------------ | |
void AC97C_Handler(void) | |
{ | |
unsigned int status; | |
// Get the real interrupt source | |
status = AT91C_BASE_AC97C->AC97C_SR; | |
status &= AT91C_BASE_AC97C->AC97C_IMR; | |
// Check if an event on the codec channel is active | |
if ((status & AT91C_AC97C_COEVT) != 0) { | |
CodecHandler(); | |
} | |
// Check if an event on channel A is active | |
if ((status & AT91C_AC97C_CAEVT) != 0) { | |
ChannelAHandler(); | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Starts a read or write transfer on the given channel | |
/// \param channel particular channel (AC97C_CODEC_TRANSFER, | |
/// AC97C_CHANNEL_A_RECEIVE, AC97C_CHANNEL_A_TRANSMIT, | |
/// AC97C_CHANNEL_B_RECEIVE or AC97C_CHANNEL_B_TRANSMIT). | |
/// \param pBuffer buffer containing the slots to send. | |
/// \param numSamples total number of samples to send. | |
/// \param callback optional callback function. | |
/// \param pArg optional argument to the callback function. | |
//------------------------------------------------------------------------------ | |
unsigned char AC97C_Transfer( | |
unsigned char channel, | |
unsigned char *pBuffer, | |
unsigned int numSamples, | |
Ac97Callback callback, | |
void *pArg) | |
{ | |
unsigned int size; | |
unsigned int data; | |
Ac97Transfer *pTransfer; | |
SANITY_CHECK(channel <= 5); | |
SANITY_CHECK(pBuffer); | |
SANITY_CHECK(numSamples > 0); | |
// Check that no transfer is pending on the channel | |
pTransfer = &(ac97c.transfers[channel]); | |
if (pTransfer->numSamples > 0) { | |
TRACE_WARNING( | |
"AC97C_Transfer: Channel %d is busy\n\r", channel); | |
return AC97C_ERROR_BUSY; | |
} | |
// Fill transfer information | |
pTransfer->pBuffer = pBuffer; | |
pTransfer->numSamples = numSamples; | |
pTransfer->callback = callback; | |
pTransfer->pArg = pArg; | |
// Transmit or receive over codec channel | |
if (channel == AC97C_CODEC_TRANSFER) { | |
// Send command | |
data = *((unsigned int *) pTransfer->pBuffer); | |
AT91C_BASE_AC97C->AC97C_COTHR = data; | |
// Check if transfer is read or write | |
if ((data & AT91C_AC97C_READ) != 0) { | |
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY; | |
} | |
else { | |
pTransfer->pBuffer += sizeof(unsigned int); | |
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY; | |
} | |
// Enable interrupts | |
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_COEVT; | |
} | |
// Transmit over channel A | |
else if (channel == AC97C_CHANNEL_A_TRANSMIT) { | |
// Disable PDC | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS; | |
// Fill PDC buffers | |
size = min(pTransfer->numSamples, MAX_PDC_COUNTER); | |
AT91C_BASE_AC97C->AC97C_TPR = (unsigned int) pTransfer->pBuffer; | |
AT91C_BASE_AC97C->AC97C_TCR = size; | |
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A); | |
size = min(pTransfer->numSamples - size, MAX_PDC_COUNTER); | |
if (size > 0) { | |
AT91C_BASE_AC97C->AC97C_TNPR = (unsigned int) pTransfer->pBuffer; | |
AT91C_BASE_AC97C->AC97C_TNCR = size; | |
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A); | |
} | |
// Enable interrupts | |
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_PDCEN | AT91C_AC97C_ENDTX; | |
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_CAEVT; | |
// Start transfer | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTEN; | |
} | |
// Receive over channel A | |
else if (channel == AC97C_CHANNEL_A_RECEIVE) { | |
// Disable PDC | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS; | |
// Fill PDC buffers | |
size = min(pTransfer->numSamples, MAX_PDC_COUNTER); | |
AT91C_BASE_AC97C->AC97C_RPR = (unsigned int) pTransfer->pBuffer; | |
AT91C_BASE_AC97C->AC97C_RCR = size; | |
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A); | |
size = min(pTransfer->numSamples - size, MAX_PDC_COUNTER); | |
if (size > 0) { | |
AT91C_BASE_AC97C->AC97C_RNPR = (unsigned int) pTransfer->pBuffer; | |
AT91C_BASE_AC97C->AC97C_RNCR = size; | |
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A); | |
} | |
// Enable interrupts | |
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_PDCEN | AT91C_AC97C_ENDRX; | |
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_CAEVT; | |
// Start transfer | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTEN; | |
} | |
return 0; | |
} | |
//------------------------------------------------------------------------------ | |
/// Stop read or write transfer on the given channel. | |
/// \param channel Channel number. | |
//------------------------------------------------------------------------------ | |
void AC97C_CancelTransfer(unsigned char channel) | |
{ | |
unsigned int size = 0; | |
Ac97Transfer *pTransfer; | |
SANITY_CHECK(channel <= AC97C_CHANNEL_B_TRANSMIT); | |
// Save remaining size | |
pTransfer = &(ac97c.transfers[channel]); | |
size = pTransfer->numSamples; | |
pTransfer->numSamples = 0; | |
// Stop PDC | |
if (channel == AC97C_CHANNEL_A_TRANSMIT) { | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS; | |
size -= min(size, MAX_PDC_COUNTER) - AT91C_BASE_AC97C->AC97C_TCR; | |
} | |
if (channel == AC97C_CHANNEL_A_RECEIVE) { | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS; | |
size -= min(size, MAX_PDC_COUNTER) - AT91C_BASE_AC97C->AC97C_RCR; | |
} | |
// Invoke callback if provided | |
if (pTransfer->callback) { | |
pTransfer->callback(pTransfer->pArg, AC97C_ERROR_STOPPED, size); | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Initializes the AC97 controller. | |
//------------------------------------------------------------------------------ | |
void AC97C_Configure(void) | |
{ | |
unsigned char channel; | |
// Enable the AC97 controller peripheral clock | |
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_AC97C); | |
// Enable the peripheral and variable rate adjustment | |
AT91C_BASE_AC97C->AC97C_MR = AT91C_AC97C_ENA | AT91C_AC97C_VRA; | |
// Unassigns all input & output slots | |
AC97C_AssignInputSlots(0, 0xFFFF); | |
AC97C_AssignOutputSlots(0, 0xFFFF); | |
// Install the AC97C interrupt handler | |
AT91C_BASE_AC97C->AC97C_IDR = 0xFFFFFFFF; | |
AIC_ConfigureIT(AT91C_ID_AC97C, 0, AC97C_Handler); | |
AIC_EnableIT(AT91C_ID_AC97C); | |
// Disable PDC transfers | |
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS; | |
// Clear channel transfers | |
for (channel = 0; channel < AC97C_CHANNEL_B_TRANSMIT; channel++) { | |
ac97c.transfers[channel].numSamples = 0; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Configures the desired channel with the given value. | |
/// \param channel Channel number. | |
/// \param cfg Configuration value. | |
//------------------------------------------------------------------------------ | |
void AC97C_ConfigureChannel(unsigned char channel, unsigned int cfg) | |
{ | |
SANITY_CHECK((channel == AC97C_CHANNEL_A) || (channel == AC97C_CHANNEL_B)); | |
if (channel == AC97C_CHANNEL_A) { | |
AT91C_BASE_AC97C->AC97C_CAMR = cfg; | |
} | |
else { | |
AT91C_BASE_AC97C->AC97C_CBMR = cfg; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Assigns the desired input slots to a particular channel. | |
/// \param channel Channel number (or 0 to unassign slots). | |
/// \param slots Bitfield value of slots to assign. | |
//------------------------------------------------------------------------------ | |
void AC97C_AssignInputSlots(unsigned char channel, unsigned int slots) | |
{ | |
unsigned int value; | |
unsigned int i; | |
SANITY_CHECK(channel <= AC97C_CHANNEL_B); | |
// Assign all slots | |
slots >>= 3; | |
for (i = 3; i < 15; i++) { | |
// Check if slots is selected | |
if (slots & 1) { | |
value = AT91C_BASE_AC97C->AC97C_ICA; | |
value &= ~(0x07 << ((i - 3) * 3)); | |
value |= channel << ((i - 3) * 3); | |
AT91C_BASE_AC97C->AC97C_ICA = value; | |
} | |
slots >>= 1; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Assigns the desired output slots to a particular channel. | |
/// \param channel Channel number (or 0 to unassign slots). | |
/// \param slots Bitfield value of slots to assign. | |
//------------------------------------------------------------------------------ | |
void AC97C_AssignOutputSlots(unsigned char channel, unsigned int slots) | |
{ | |
unsigned int value; | |
unsigned int i; | |
SANITY_CHECK(channel <= AC97C_CHANNEL_B); | |
// Assign all slots | |
slots >>= 3; | |
for (i = 3; i < 15; i++) { | |
// Check if slots is selected | |
if (slots & 1) { | |
value = AT91C_BASE_AC97C->AC97C_OCA; | |
value &= ~(0x07 << ((i - 3) * 3)); | |
value |= channel << ((i - 3) * 3); | |
AT91C_BASE_AC97C->AC97C_OCA = value; | |
} | |
slots >>= 1; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Returns 1 if no transfer is currently pending on the given channel; | |
/// otherwise, returns 0. | |
/// \param channel Channel number. | |
//------------------------------------------------------------------------------ | |
unsigned char AC97C_IsFinished(unsigned char channel) | |
{ | |
SANITY_CHECK(channel <= AC97C_CHANNEL_B_TRANSMIT); | |
if (ac97c.transfers[channel].numSamples > 0) { | |
return 0; | |
} | |
else { | |
return 1; | |
} | |
} | |
//------------------------------------------------------------------------------ | |
/// Convenience function for synchronously sending commands to the codec. | |
/// \param address Register address. | |
/// \param data Command data. | |
//------------------------------------------------------------------------------ | |
void AC97C_WriteCodec(unsigned char address, unsigned short data) | |
{ | |
unsigned int sample; | |
sample = (address << 16) | data; | |
AC97C_Transfer(AC97C_CODEC_TRANSFER, (unsigned char *) &sample, 1, 0, 0); | |
while (!AC97C_IsFinished(AC97C_CODEC_TRANSFER)); | |
} | |
//------------------------------------------------------------------------------ | |
/// Convenience function for receiving data from the AC97 codec. | |
/// \param address Register address. | |
//------------------------------------------------------------------------------ | |
unsigned short AC97C_ReadCodec(unsigned char address) | |
{ | |
unsigned int sample; | |
sample = AT91C_AC97C_READ | (address << 16); | |
AC97C_Transfer(AC97C_CODEC_TRANSFER, (unsigned char *) &sample, 1, 0, 0); | |
while (!AC97C_IsFinished(AC97C_CODEC_TRANSFER)); | |
return sample; | |
} | |
//------------------------------------------------------------------------------ | |
/// Sets the size in bits of one sample on the given channel. | |
/// \param channel Channel number. | |
/// \param size Size of one sample in bits (10, 16, 18 or 24). | |
//------------------------------------------------------------------------------ | |
void AC97C_SetChannelSize(unsigned char channel, unsigned char size) | |
{ | |
unsigned int bits = 0; | |
SANITY_CHECK((size == 10) || (size == 16) || (size == 18) || (size == 24)); | |
SANITY_CHECK((channel == AC97C_CHANNEL_A) || (channel == AC97C_CHANNEL_B)); | |
switch (size) { | |
case 10 : bits = AT91C_AC97C_SIZE_10_BITS; break; | |
case 16 : bits = AT91C_AC97C_SIZE_16_BITS; break; | |
case 18 : bits = AT91C_AC97C_SIZE_18_BITS; break; | |
case 20 : bits = AT91C_AC97C_SIZE_20_BITS; break; | |
} | |
if (channel == AC97C_CHANNEL_A) { | |
AT91C_BASE_AC97C->AC97C_CAMR &= ~(AT91C_AC97C_SIZE); | |
AT91C_BASE_AC97C->AC97C_CAMR |= bits; | |
} | |
else { | |
AT91C_BASE_AC97C->AC97C_CBMR &= ~(AT91C_AC97C_SIZE); | |
AT91C_BASE_AC97C->AC97C_CBMR |= bits; | |
} | |
} | |