summaryrefslogtreecommitdiff
path: root/peripherals/ac97c
diff options
context:
space:
mode:
Diffstat (limited to 'peripherals/ac97c')
-rw-r--r--peripherals/ac97c/ac97c.c692
-rw-r--r--peripherals/ac97c/ac97c.dir37
-rw-r--r--peripherals/ac97c/ac97c.h168
3 files changed, 897 insertions, 0 deletions
diff --git a/peripherals/ac97c/ac97c.c b/peripherals/ac97c/ac97c.c
new file mode 100644
index 0000000..bfc3883
--- /dev/null
+++ b/peripherals/ac97c/ac97c.c
@@ -0,0 +1,692 @@
+/* ----------------------------------------------------------------------------
+ * 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 <irq/irq.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;
+ IRQ_ConfigureIT(AT91C_ID_AC97C, 0, AC97C_Handler);
+ IRQ_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;
+ }
+}
+
diff --git a/peripherals/ac97c/ac97c.dir b/peripherals/ac97c/ac97c.dir
new file mode 100644
index 0000000..ac7e2de
--- /dev/null
+++ b/peripherals/ac97c/ac97c.dir
@@ -0,0 +1,37 @@
+/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \dir
+///
+/// !!!Purpose
+///
+/// This directory contains an API for configuring the Peripheral AC'97
+/// controller (AC97C).
+//------------------------------------------------------------------------------ \ No newline at end of file
diff --git a/peripherals/ac97c/ac97c.h b/peripherals/ac97c/ac97c.h
new file mode 100644
index 0000000..1dbb1a8
--- /dev/null
+++ b/peripherals/ac97c/ac97c.h
@@ -0,0 +1,168 @@
+/* ----------------------------------------------------------------------------
+ * 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.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+/// \unit
+///
+/// !!!Purpose
+///
+/// This module provides definitions and functions for using the AC'97
+/// controller (AC97C).
+///
+/// !!!Usage
+///
+/// -# Enable the AC'97 interface pins (see pio & board.h).
+/// -# Configure the AC'97 controller using AC97C_Configure
+/// -# Assign the input and output slots to channels, and the data size used to
+/// transfer AC97 data stream.
+/// - Three functions can be used:
+/// - AC97C_AssignInputSlots: set slots for AC'97 data in, recording.
+/// - AC97C_AssignOutputSlots: set slots for AC'97 data out, playing.
+/// - AC97C_SetChannelSize: set data sizes in bits for AC'97 data stream.
+/// - Three different channels can be configured:
+/// - AC97C_CHANNEL_CODEC: AC'97 register access channel, its size is
+/// fixed and #must not# change by AC97C_SetChannelSize
+/// - AC97C_CHANNEL_A: AC'97 stream channel, with PDC support.
+/// - AC97C_CHANNEL_B: AC'97 data channel, without PDC support.
+/// -# Configure the used AC97 channel with AC97C_ConfigureChannel, to enable
+/// the channel.
+/// -# Then you can operate the connected AC'97 codec:
+/// - AC97C_ReadCodec / AC97C_WriteCodec: Read / Write codec register.
+/// - AC97C_Transfer: Transfer through AC97C channels to setup codec register
+/// or transfer %audio data stream.
+/// - AC97C_CODEC_TRANSFER: access the codec register.
+/// - AC97C_CHANNEL_A_RECEIVE, AC97C_CHANNEL_B_RECEIVE: start reading.
+/// - AC97C_CHANNEL_A_TRANSMIT, AC97C_CHANNEL_B_TRANSMIT: start writing.
+/// Normally you can initialize a set of AC'97 codec registers to initialize
+/// the codec for %audio playing and recording.
+/// -# Example code for playing & recording:
+/// - General process:
+/// -# Configure the codec registers for the %audio settings and formats;
+/// -# Setup the channel size if necessery;
+/// -# Start %audio stream transfer.
+/// - Audio playing sample:
+/// \code
+/// // Configure sample rate of codec
+/// AC97C_WriteCodec(AD1981B_PMC_DAC, expectedSampleRate);
+/// // Set channel size
+/// AC97C_SetChannelSize(AC97C_CHANNEL_A, bitsPerSample);
+/// // Start channel A transfer
+/// AC97C_Transfer(AC97C_CHANNEL_A_TRANSMIT,
+/// (unsigned char *) (pointerToAudioDataBuffer),
+/// numberOfSamplesToSend,
+/// (Ac97Callback) PlayingFinished,
+/// 0);
+/// \endcode
+/// - Audio recording sample:
+/// \code
+/// // Enable recording
+/// AC97C_WriteCodec(AD1981B_REC_SEL, 0);
+/// // Set sample rate
+/// AC97C_WriteCodec(AD1981B_PMC_ADC, 7000);
+/// // Always use 16-bits recording
+/// AC97C_SetChannelSize(AC97C_CHANNEL_A, 16);
+/// // Start recording
+/// AC97C_Transfer(AC97C_CHANNEL_A_RECEIVE,
+/// (unsigned char *) RECORD_ADDRESS,
+/// MAX_RECORD_SIZE,
+/// (Ac97Callback) RecordFinished,
+/// 0);
+/// \endcode
+//------------------------------------------------------------------------------
+
+#ifndef AC97C_H
+#define AC97C_H
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+/// The channel is already busy with a transfer.
+#define AC97C_ERROR_BUSY 1
+/// The transfer has been stopped by the user.
+#define AC97C_ERROR_STOPPED 2
+
+/// Codec channel index.
+#define AC97C_CHANNEL_CODEC 0
+/// Channel A index.
+#define AC97C_CHANNEL_A 1
+/// Channel B index.
+#define AC97C_CHANNEL_B 2
+
+/// Codec transmit/receive transfer index.
+#define AC97C_CODEC_TRANSFER 0
+/// Channel A receive transfer index.
+#define AC97C_CHANNEL_A_RECEIVE 1
+/// Channel A transmit transfer index.
+#define AC97C_CHANNEL_A_TRANSMIT 2
+/// Channel B receive transfer index.
+#define AC97C_CHANNEL_B_RECEIVE 3
+/// Channel B transmit transfer index.
+#define AC97C_CHANNEL_B_TRANSMIT 4
+
+//------------------------------------------------------------------------------
+// Types
+//------------------------------------------------------------------------------
+
+/// AC97C transfer callback function.
+typedef void (*Ac97Callback)(void *pArg,
+ unsigned char status,
+ unsigned int remaining);
+
+//------------------------------------------------------------------------------
+// Exported functions
+//------------------------------------------------------------------------------
+
+extern void AC97C_Configure();
+
+extern void AC97C_ConfigureChannel(unsigned char channel, unsigned int cfg);
+
+extern void AC97C_AssignInputSlots(unsigned char channel, unsigned int slots);
+
+extern void AC97C_AssignOutputSlots(unsigned char channel, unsigned int slots);
+
+extern unsigned char AC97C_Transfer(
+ unsigned char channel,
+ unsigned char *pBuffer,
+ unsigned int numSamples,
+ Ac97Callback callback,
+ void *pArg);
+
+extern unsigned char AC97C_IsFinished(unsigned char channel);
+
+extern void AC97C_WriteCodec(unsigned char address, unsigned short data);
+
+extern unsigned short AC97C_ReadCodec(unsigned char address);
+
+extern void AC97C_SetChannelSize(unsigned char channel, unsigned char size);
+
+extern void AC97C_CancelTransfer(unsigned char channel);
+
+#endif //#ifndef AC97C_H
+
personal git repositories of Harald Welte. Your mileage may vary