?? ac97c.c
字號:
//------------------------------------------------------------------------------
/// Starts a read or write transfer on the given channel
/// \param channel particular channel (AC97C_CHANNEL_A or AC97C_CHANNEL_B).
/// \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_LOG(trace_WARNING, "-W- 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;
}
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -