From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- peripherals/ac97c/ac97c.c | 692 ++++++++++++++++++++++++++++++++++++++++++++ peripherals/ac97c/ac97c.dir | 37 +++ peripherals/ac97c/ac97c.h | 168 +++++++++++ 3 files changed, 897 insertions(+) create mode 100644 peripherals/ac97c/ac97c.c create mode 100644 peripherals/ac97c/ac97c.dir create mode 100644 peripherals/ac97c/ac97c.h (limited to 'peripherals/ac97c') 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 +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// 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 + -- cgit v1.2.3