1 |
608 |
jeremybenn |
/* ----------------------------------------------------------------------------
|
2 |
|
|
* ATMEL Microcontroller Software Support
|
3 |
|
|
* ----------------------------------------------------------------------------
|
4 |
|
|
* Copyright (c) 2008, Atmel Corporation
|
5 |
|
|
*
|
6 |
|
|
* All rights reserved.
|
7 |
|
|
*
|
8 |
|
|
* Redistribution and use in source and binary forms, with or without
|
9 |
|
|
* modification, are permitted provided that the following conditions are met:
|
10 |
|
|
*
|
11 |
|
|
* - Redistributions of source code must retain the above copyright notice,
|
12 |
|
|
* this list of conditions and the disclaimer below.
|
13 |
|
|
*
|
14 |
|
|
* Atmel's name may not be used to endorse or promote products derived from
|
15 |
|
|
* this software without specific prior written permission.
|
16 |
|
|
*
|
17 |
|
|
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
|
18 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
19 |
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
|
20 |
|
|
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
|
21 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 |
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
23 |
|
|
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24 |
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25 |
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
26 |
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27 |
|
|
* ----------------------------------------------------------------------------
|
28 |
|
|
*/
|
29 |
|
|
|
30 |
|
|
//------------------------------------------------------------------------------
|
31 |
|
|
// Headers
|
32 |
|
|
//------------------------------------------------------------------------------
|
33 |
|
|
|
34 |
|
|
#include "ac97c.h"
|
35 |
|
|
#include <board.h>
|
36 |
|
|
#include <aic/aic.h>
|
37 |
|
|
#include <utility/assert.h>
|
38 |
|
|
#include <utility/trace.h>
|
39 |
|
|
#include <utility/math.h>
|
40 |
|
|
|
41 |
|
|
//------------------------------------------------------------------------------
|
42 |
|
|
// Local constants
|
43 |
|
|
//------------------------------------------------------------------------------
|
44 |
|
|
|
45 |
|
|
/// Maximum size of one PDC buffer (in bytes).
|
46 |
|
|
#define MAX_PDC_COUNTER 65535
|
47 |
|
|
|
48 |
|
|
//------------------------------------------------------------------------------
|
49 |
|
|
// Local types
|
50 |
|
|
//------------------------------------------------------------------------------
|
51 |
|
|
|
52 |
|
|
//------------------------------------------------------------------------------
|
53 |
|
|
/// AC97 transfer descriptor. Tracks the status and parameters of a transfer
|
54 |
|
|
/// on the AC97 bus.
|
55 |
|
|
//------------------------------------------------------------------------------
|
56 |
|
|
typedef struct _Ac97Transfer {
|
57 |
|
|
|
58 |
|
|
/// Buffer containing the slots to send.
|
59 |
|
|
unsigned char *pBuffer;
|
60 |
|
|
/// Total number of samples to send.
|
61 |
|
|
volatile unsigned int numSamples;
|
62 |
|
|
/// Optional callback function.
|
63 |
|
|
Ac97Callback callback;
|
64 |
|
|
/// Optional argument to the callback function.
|
65 |
|
|
void *pArg;
|
66 |
|
|
|
67 |
|
|
} Ac97Transfer;
|
68 |
|
|
|
69 |
|
|
//------------------------------------------------------------------------------
|
70 |
|
|
/// AC97 controller driver structure. Monitors the status of transfers on all
|
71 |
|
|
/// AC97 channels.
|
72 |
|
|
//------------------------------------------------------------------------------
|
73 |
|
|
typedef struct _Ac97c {
|
74 |
|
|
|
75 |
|
|
/// List of transfers occuring on each channel.
|
76 |
|
|
Ac97Transfer transfers[5];
|
77 |
|
|
} Ac97c;
|
78 |
|
|
|
79 |
|
|
//------------------------------------------------------------------------------
|
80 |
|
|
// Local variables
|
81 |
|
|
//------------------------------------------------------------------------------
|
82 |
|
|
|
83 |
|
|
/// Global AC97 controller instance.
|
84 |
|
|
static Ac97c ac97c;
|
85 |
|
|
|
86 |
|
|
//------------------------------------------------------------------------------
|
87 |
|
|
// Local functions
|
88 |
|
|
//------------------------------------------------------------------------------
|
89 |
|
|
|
90 |
|
|
//------------------------------------------------------------------------------
|
91 |
|
|
/// Returns the size of one sample (in bytes) on the given channel.
|
92 |
|
|
/// \param channel Channel number.
|
93 |
|
|
//------------------------------------------------------------------------------
|
94 |
|
|
static unsigned char GetSampleSize(unsigned char channel)
|
95 |
|
|
{
|
96 |
|
|
unsigned int size = 0;
|
97 |
|
|
|
98 |
|
|
SANITY_CHECK((channel == AC97C_CHANNEL_A)
|
99 |
|
|
|| (channel == AC97C_CHANNEL_B)
|
100 |
|
|
|| (channel == AC97C_CHANNEL_CODEC));
|
101 |
|
|
|
102 |
|
|
// Check selected channel
|
103 |
|
|
switch (channel) {
|
104 |
|
|
case AC97C_CHANNEL_CODEC: return 2;
|
105 |
|
|
case AC97C_CHANNEL_A: size = (AT91C_BASE_AC97C->AC97C_CAMR & AT91C_AC97C_SIZE) >> 16; break;
|
106 |
|
|
case AC97C_CHANNEL_B: size = (AT91C_BASE_AC97C->AC97C_CBMR & AT91C_AC97C_SIZE) >> 16; break;
|
107 |
|
|
}
|
108 |
|
|
|
109 |
|
|
// Compute size in bytes given SIZE field
|
110 |
|
|
if ((size & 2) != 0) {
|
111 |
|
|
|
112 |
|
|
return 2;
|
113 |
|
|
}
|
114 |
|
|
else {
|
115 |
|
|
|
116 |
|
|
return 4;
|
117 |
|
|
}
|
118 |
|
|
}
|
119 |
|
|
|
120 |
|
|
//------------------------------------------------------------------------------
|
121 |
|
|
/// Interrupt service routine for Codec, is invoked by AC97C_Handler.
|
122 |
|
|
//------------------------------------------------------------------------------
|
123 |
|
|
static void CodecHandler(void)
|
124 |
|
|
{
|
125 |
|
|
unsigned int status;
|
126 |
|
|
unsigned int data;
|
127 |
|
|
Ac97Transfer *pTransfer = &(ac97c.transfers[AC97C_CODEC_TRANSFER]);
|
128 |
|
|
|
129 |
|
|
// Read CODEC status register
|
130 |
|
|
status = AT91C_BASE_AC97C->AC97C_COSR;
|
131 |
|
|
status &= AT91C_BASE_AC97C->AC97C_COMR;
|
132 |
|
|
|
133 |
|
|
// A sample has been transmitted
|
134 |
|
|
if (status & AT91C_AC97C_TXRDY) {
|
135 |
|
|
|
136 |
|
|
pTransfer->numSamples--;
|
137 |
|
|
|
138 |
|
|
// If there are remaining samples, transmit one
|
139 |
|
|
if (pTransfer->numSamples > 0) {
|
140 |
|
|
|
141 |
|
|
data = *((unsigned int *) pTransfer->pBuffer);
|
142 |
|
|
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_TXRDY);
|
143 |
|
|
AT91C_BASE_AC97C->AC97C_COTHR = data;
|
144 |
|
|
|
145 |
|
|
// Check if transfer is read or write
|
146 |
|
|
if ((data & AT91C_AC97C_READ) != 0) {
|
147 |
|
|
|
148 |
|
|
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY;
|
149 |
|
|
}
|
150 |
|
|
else {
|
151 |
|
|
|
152 |
|
|
pTransfer->pBuffer += sizeof(unsigned int);
|
153 |
|
|
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY;
|
154 |
|
|
}
|
155 |
|
|
}
|
156 |
|
|
// Transfer finished
|
157 |
|
|
else {
|
158 |
|
|
|
159 |
|
|
AT91C_BASE_AC97C->AC97C_IDR = AT91C_AC97C_COEVT;
|
160 |
|
|
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_TXRDY);
|
161 |
|
|
if (pTransfer->callback) {
|
162 |
|
|
|
163 |
|
|
pTransfer->callback(pTransfer->pArg, 0, 0);
|
164 |
|
|
}
|
165 |
|
|
}
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
// A sample has been received
|
169 |
|
|
if (status & AT91C_AC97C_RXRDY) {
|
170 |
|
|
|
171 |
|
|
// Store sample
|
172 |
|
|
data = AT91C_BASE_AC97C->AC97C_CORHR;
|
173 |
|
|
*((unsigned int *) pTransfer->pBuffer) = data;
|
174 |
|
|
|
175 |
|
|
pTransfer->pBuffer += sizeof(unsigned int);
|
176 |
|
|
pTransfer->numSamples--;
|
177 |
|
|
|
178 |
|
|
// Transfer finished
|
179 |
|
|
if (pTransfer->numSamples > 0) {
|
180 |
|
|
|
181 |
|
|
data = *((unsigned int *) pTransfer->pBuffer);
|
182 |
|
|
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_RXRDY);
|
183 |
|
|
AT91C_BASE_AC97C->AC97C_COTHR = data;
|
184 |
|
|
|
185 |
|
|
// Check if transfer is read or write
|
186 |
|
|
if ((data & AT91C_AC97C_READ) != 0) {
|
187 |
|
|
|
188 |
|
|
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY;
|
189 |
|
|
}
|
190 |
|
|
else {
|
191 |
|
|
|
192 |
|
|
pTransfer->pBuffer += sizeof(unsigned int);
|
193 |
|
|
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY;
|
194 |
|
|
}
|
195 |
|
|
}
|
196 |
|
|
else {
|
197 |
|
|
|
198 |
|
|
AT91C_BASE_AC97C->AC97C_IDR = AT91C_AC97C_COEVT;
|
199 |
|
|
AT91C_BASE_AC97C->AC97C_COMR &= ~(AT91C_AC97C_RXRDY);
|
200 |
|
|
if (pTransfer->callback) {
|
201 |
|
|
|
202 |
|
|
pTransfer->callback(pTransfer->pArg, 0, 0);
|
203 |
|
|
}
|
204 |
|
|
}
|
205 |
|
|
}
|
206 |
|
|
}
|
207 |
|
|
|
208 |
|
|
//------------------------------------------------------------------------------
|
209 |
|
|
/// Interrupt service routine for channel A, is invoked by AC97C_Handler.
|
210 |
|
|
//------------------------------------------------------------------------------
|
211 |
|
|
static void ChannelAHandler(void)
|
212 |
|
|
{
|
213 |
|
|
unsigned int status;
|
214 |
|
|
Ac97Transfer *pTransmit = &(ac97c.transfers[AC97C_CHANNEL_A_TRANSMIT]);
|
215 |
|
|
Ac97Transfer *pReceive = &(ac97c.transfers[AC97C_CHANNEL_A_RECEIVE]);
|
216 |
|
|
|
217 |
|
|
// Read channel A status register
|
218 |
|
|
status = AT91C_BASE_AC97C->AC97C_CASR;
|
219 |
|
|
|
220 |
|
|
// A buffer has been transmitted
|
221 |
|
|
if ((status & AT91C_AC97C_ENDTX) != 0) {
|
222 |
|
|
|
223 |
|
|
// Update transfer information
|
224 |
|
|
if (pTransmit->numSamples > MAX_PDC_COUNTER) {
|
225 |
|
|
|
226 |
|
|
pTransmit->numSamples -= MAX_PDC_COUNTER;
|
227 |
|
|
}
|
228 |
|
|
else {
|
229 |
|
|
|
230 |
|
|
pTransmit->numSamples = 0;
|
231 |
|
|
}
|
232 |
|
|
|
233 |
|
|
// Transmit new buffers if necessary
|
234 |
|
|
if (pTransmit->numSamples > MAX_PDC_COUNTER) {
|
235 |
|
|
|
236 |
|
|
// Fill next PDC
|
237 |
|
|
AT91C_BASE_AC97C->AC97C_TNPR = (unsigned int) pTransmit->pBuffer;
|
238 |
|
|
if (pTransmit->numSamples > 2 * MAX_PDC_COUNTER) {
|
239 |
|
|
|
240 |
|
|
AT91C_BASE_AC97C->AC97C_TNCR = MAX_PDC_COUNTER;
|
241 |
|
|
pTransmit->pBuffer += MAX_PDC_COUNTER * GetSampleSize(AC97C_CHANNEL_A);
|
242 |
|
|
}
|
243 |
|
|
else {
|
244 |
|
|
|
245 |
|
|
AT91C_BASE_AC97C->AC97C_TNCR = pTransmit->numSamples - MAX_PDC_COUNTER;
|
246 |
|
|
}
|
247 |
|
|
}
|
248 |
|
|
// Only one buffer remaining
|
249 |
|
|
else {
|
250 |
|
|
|
251 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_ENDTX;
|
252 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_TXBUFE;
|
253 |
|
|
}
|
254 |
|
|
}
|
255 |
|
|
|
256 |
|
|
// Transmit completed
|
257 |
|
|
if ((status & AT91C_AC97C_TXBUFE) != 0) {
|
258 |
|
|
|
259 |
|
|
pTransmit->numSamples = 0;
|
260 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS;
|
261 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_TXBUFE;
|
262 |
|
|
if (pTransmit->callback) {
|
263 |
|
|
|
264 |
|
|
pTransmit->callback(pTransmit->pArg, 0, 0);
|
265 |
|
|
}
|
266 |
|
|
}
|
267 |
|
|
|
268 |
|
|
// A buffer has been received
|
269 |
|
|
if (status & AT91C_AC97C_ENDRX) {
|
270 |
|
|
|
271 |
|
|
if (pReceive->numSamples > MAX_PDC_COUNTER) {
|
272 |
|
|
|
273 |
|
|
pReceive->numSamples -= MAX_PDC_COUNTER;
|
274 |
|
|
}
|
275 |
|
|
else {
|
276 |
|
|
|
277 |
|
|
pReceive->numSamples = 0;
|
278 |
|
|
}
|
279 |
|
|
|
280 |
|
|
// Transfer remaining samples
|
281 |
|
|
if (pReceive->numSamples > MAX_PDC_COUNTER) {
|
282 |
|
|
|
283 |
|
|
AT91C_BASE_AC97C->AC97C_RNPR = (unsigned int) pReceive->pBuffer;
|
284 |
|
|
if (pReceive->numSamples > 2 * MAX_PDC_COUNTER) {
|
285 |
|
|
|
286 |
|
|
AT91C_BASE_AC97C->AC97C_RNCR = MAX_PDC_COUNTER;
|
287 |
|
|
pReceive->pBuffer += MAX_PDC_COUNTER * GetSampleSize(AC97C_CHANNEL_A);
|
288 |
|
|
}
|
289 |
|
|
else {
|
290 |
|
|
|
291 |
|
|
AT91C_BASE_AC97C->AC97C_RNCR = pReceive->numSamples - MAX_PDC_COUNTER;
|
292 |
|
|
}
|
293 |
|
|
}
|
294 |
|
|
// Only one buffer remaining
|
295 |
|
|
else {
|
296 |
|
|
|
297 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR &= ~(AT91C_AC97C_ENDRX);
|
298 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_RXBUFF;
|
299 |
|
|
}
|
300 |
|
|
}
|
301 |
|
|
|
302 |
|
|
// Receive complete
|
303 |
|
|
if ((status & AT91C_AC97C_RXBUFF) != 0) {
|
304 |
|
|
|
305 |
|
|
pReceive->numSamples = 0;
|
306 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS;
|
307 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR &= ~AT91C_AC97C_RXBUFF;
|
308 |
|
|
if (pReceive->callback) {
|
309 |
|
|
|
310 |
|
|
pReceive->callback(pReceive->pArg, 0, 0);
|
311 |
|
|
}
|
312 |
|
|
}
|
313 |
|
|
}
|
314 |
|
|
|
315 |
|
|
//------------------------------------------------------------------------------
|
316 |
|
|
// Exported functions
|
317 |
|
|
//------------------------------------------------------------------------------
|
318 |
|
|
//------------------------------------------------------------------------------
|
319 |
|
|
/// This handler function must be called by the AC97C interrupt service routine.
|
320 |
|
|
/// Identifies which event was activated and calls the associated function.
|
321 |
|
|
//------------------------------------------------------------------------------
|
322 |
|
|
void AC97C_Handler(void)
|
323 |
|
|
{
|
324 |
|
|
unsigned int status;
|
325 |
|
|
|
326 |
|
|
// Get the real interrupt source
|
327 |
|
|
status = AT91C_BASE_AC97C->AC97C_SR;
|
328 |
|
|
status &= AT91C_BASE_AC97C->AC97C_IMR;
|
329 |
|
|
|
330 |
|
|
// Check if an event on the codec channel is active
|
331 |
|
|
if ((status & AT91C_AC97C_COEVT) != 0) {
|
332 |
|
|
|
333 |
|
|
CodecHandler();
|
334 |
|
|
}
|
335 |
|
|
// Check if an event on channel A is active
|
336 |
|
|
if ((status & AT91C_AC97C_CAEVT) != 0) {
|
337 |
|
|
|
338 |
|
|
ChannelAHandler();
|
339 |
|
|
}
|
340 |
|
|
}
|
341 |
|
|
|
342 |
|
|
//------------------------------------------------------------------------------
|
343 |
|
|
/// Starts a read or write transfer on the given channel
|
344 |
|
|
/// \param channel particular channel (AC97C_CHANNEL_A or AC97C_CHANNEL_B).
|
345 |
|
|
/// \param pBuffer buffer containing the slots to send.
|
346 |
|
|
/// \param numSamples total number of samples to send.
|
347 |
|
|
/// \param callback optional callback function.
|
348 |
|
|
/// \param pArg optional argument to the callback function.
|
349 |
|
|
//------------------------------------------------------------------------------
|
350 |
|
|
unsigned char AC97C_Transfer(
|
351 |
|
|
unsigned char channel,
|
352 |
|
|
unsigned char *pBuffer,
|
353 |
|
|
unsigned int numSamples,
|
354 |
|
|
Ac97Callback callback,
|
355 |
|
|
void *pArg)
|
356 |
|
|
{
|
357 |
|
|
unsigned int size;
|
358 |
|
|
unsigned int data;
|
359 |
|
|
Ac97Transfer *pTransfer;
|
360 |
|
|
|
361 |
|
|
SANITY_CHECK(channel <= 5);
|
362 |
|
|
SANITY_CHECK(pBuffer);
|
363 |
|
|
SANITY_CHECK(numSamples > 0);
|
364 |
|
|
|
365 |
|
|
// Check that no transfer is pending on the channel
|
366 |
|
|
pTransfer = &(ac97c.transfers[channel]);
|
367 |
|
|
if (pTransfer->numSamples > 0) {
|
368 |
|
|
|
369 |
|
|
trace_LOG(trace_WARNING, "-W- AC97C_Transfer: Channel %d is busy\n\r", channel);
|
370 |
|
|
return AC97C_ERROR_BUSY;
|
371 |
|
|
}
|
372 |
|
|
|
373 |
|
|
// Fill transfer information
|
374 |
|
|
pTransfer->pBuffer = pBuffer;
|
375 |
|
|
pTransfer->numSamples = numSamples;
|
376 |
|
|
pTransfer->callback = callback;
|
377 |
|
|
pTransfer->pArg = pArg;
|
378 |
|
|
|
379 |
|
|
// Transmit or receive over codec channel
|
380 |
|
|
if (channel == AC97C_CODEC_TRANSFER) {
|
381 |
|
|
|
382 |
|
|
// Send command
|
383 |
|
|
data = *((unsigned int *) pTransfer->pBuffer);
|
384 |
|
|
AT91C_BASE_AC97C->AC97C_COTHR = data;
|
385 |
|
|
|
386 |
|
|
// Check if transfer is read or write
|
387 |
|
|
if ((data & AT91C_AC97C_READ) != 0) {
|
388 |
|
|
|
389 |
|
|
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_RXRDY;
|
390 |
|
|
}
|
391 |
|
|
else {
|
392 |
|
|
|
393 |
|
|
pTransfer->pBuffer += sizeof(unsigned int);
|
394 |
|
|
AT91C_BASE_AC97C->AC97C_COMR |= AT91C_AC97C_TXRDY;
|
395 |
|
|
}
|
396 |
|
|
|
397 |
|
|
// Enable interrupts
|
398 |
|
|
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_COEVT;
|
399 |
|
|
}
|
400 |
|
|
// Transmit over channel A
|
401 |
|
|
else if (channel == AC97C_CHANNEL_A_TRANSMIT) {
|
402 |
|
|
|
403 |
|
|
// Disable PDC
|
404 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS;
|
405 |
|
|
|
406 |
|
|
// Fill PDC buffers
|
407 |
|
|
size = min(pTransfer->numSamples, MAX_PDC_COUNTER);
|
408 |
|
|
AT91C_BASE_AC97C->AC97C_TPR = (unsigned int) pTransfer->pBuffer;
|
409 |
|
|
AT91C_BASE_AC97C->AC97C_TCR = size;
|
410 |
|
|
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
|
411 |
|
|
|
412 |
|
|
size = min(pTransfer->numSamples - size, MAX_PDC_COUNTER);
|
413 |
|
|
if (size > 0) {
|
414 |
|
|
|
415 |
|
|
AT91C_BASE_AC97C->AC97C_TNPR = (unsigned int) pTransfer->pBuffer;
|
416 |
|
|
AT91C_BASE_AC97C->AC97C_TNCR = size;
|
417 |
|
|
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
|
418 |
|
|
}
|
419 |
|
|
|
420 |
|
|
// Enable interrupts
|
421 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_PDCEN | AT91C_AC97C_ENDTX;
|
422 |
|
|
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_CAEVT;
|
423 |
|
|
|
424 |
|
|
// Start transfer
|
425 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTEN;
|
426 |
|
|
}
|
427 |
|
|
// Receive over channel A
|
428 |
|
|
else if (channel == AC97C_CHANNEL_A_RECEIVE) {
|
429 |
|
|
|
430 |
|
|
// Disable PDC
|
431 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS;
|
432 |
|
|
|
433 |
|
|
// Fill PDC buffers
|
434 |
|
|
size = min(pTransfer->numSamples, MAX_PDC_COUNTER);
|
435 |
|
|
AT91C_BASE_AC97C->AC97C_RPR = (unsigned int) pTransfer->pBuffer;
|
436 |
|
|
AT91C_BASE_AC97C->AC97C_RCR = size;
|
437 |
|
|
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
|
438 |
|
|
|
439 |
|
|
size = min(pTransfer->numSamples - size, MAX_PDC_COUNTER);
|
440 |
|
|
if (size > 0) {
|
441 |
|
|
|
442 |
|
|
AT91C_BASE_AC97C->AC97C_RNPR = (unsigned int) pTransfer->pBuffer;
|
443 |
|
|
AT91C_BASE_AC97C->AC97C_RNCR = size;
|
444 |
|
|
pTransfer->pBuffer += size * GetSampleSize(AC97C_CHANNEL_A);
|
445 |
|
|
}
|
446 |
|
|
|
447 |
|
|
// Enable interrupts
|
448 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR |= AT91C_AC97C_PDCEN | AT91C_AC97C_ENDRX;
|
449 |
|
|
AT91C_BASE_AC97C->AC97C_IER |= AT91C_AC97C_CAEVT;
|
450 |
|
|
|
451 |
|
|
// Start transfer
|
452 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTEN;
|
453 |
|
|
}
|
454 |
|
|
|
455 |
|
|
return 0;
|
456 |
|
|
}
|
457 |
|
|
|
458 |
|
|
//------------------------------------------------------------------------------
|
459 |
|
|
/// Stop read or write transfer on the given channel.
|
460 |
|
|
/// \param channel Channel number.
|
461 |
|
|
//------------------------------------------------------------------------------
|
462 |
|
|
void AC97C_CancelTransfer(unsigned char channel)
|
463 |
|
|
{
|
464 |
|
|
unsigned int size = 0;
|
465 |
|
|
Ac97Transfer *pTransfer;
|
466 |
|
|
|
467 |
|
|
SANITY_CHECK(channel <= AC97C_CHANNEL_B_TRANSMIT);
|
468 |
|
|
|
469 |
|
|
// Save remaining size
|
470 |
|
|
pTransfer = &(ac97c.transfers[channel]);
|
471 |
|
|
size = pTransfer->numSamples;
|
472 |
|
|
pTransfer->numSamples = 0;
|
473 |
|
|
|
474 |
|
|
// Stop PDC
|
475 |
|
|
if (channel == AC97C_CHANNEL_A_TRANSMIT) {
|
476 |
|
|
|
477 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS;
|
478 |
|
|
size -= min(size, MAX_PDC_COUNTER) - AT91C_BASE_AC97C->AC97C_TCR;
|
479 |
|
|
}
|
480 |
|
|
if (channel == AC97C_CHANNEL_A_RECEIVE) {
|
481 |
|
|
|
482 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_RXTDIS;
|
483 |
|
|
size -= min(size, MAX_PDC_COUNTER) - AT91C_BASE_AC97C->AC97C_RCR;
|
484 |
|
|
}
|
485 |
|
|
|
486 |
|
|
// Invoke callback if provided
|
487 |
|
|
if (pTransfer->callback) {
|
488 |
|
|
|
489 |
|
|
pTransfer->callback(pTransfer->pArg, AC97C_ERROR_STOPPED, size);
|
490 |
|
|
}
|
491 |
|
|
}
|
492 |
|
|
|
493 |
|
|
//------------------------------------------------------------------------------
|
494 |
|
|
/// Initializes the AC97 controller.
|
495 |
|
|
//------------------------------------------------------------------------------
|
496 |
|
|
void AC97C_Configure(void)
|
497 |
|
|
{
|
498 |
|
|
unsigned char channel;
|
499 |
|
|
|
500 |
|
|
// Enable the AC97 controller peripheral clock
|
501 |
|
|
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_AC97C);
|
502 |
|
|
|
503 |
|
|
// Enable the peripheral and variable rate adjustment
|
504 |
|
|
AT91C_BASE_AC97C->AC97C_MR = AT91C_AC97C_ENA | AT91C_AC97C_VRA;
|
505 |
|
|
|
506 |
|
|
// Unassigns all input & output slots
|
507 |
|
|
AC97C_AssignInputSlots(0, 0xFFFF);
|
508 |
|
|
AC97C_AssignOutputSlots(0, 0xFFFF);
|
509 |
|
|
|
510 |
|
|
// Install the AC97C interrupt handler
|
511 |
|
|
AT91C_BASE_AC97C->AC97C_IDR = 0xFFFFFFFF;
|
512 |
|
|
AIC_ConfigureIT(AT91C_ID_AC97C, 0, AC97C_Handler);
|
513 |
|
|
AIC_EnableIT(AT91C_ID_AC97C);
|
514 |
|
|
|
515 |
|
|
// Disable PDC transfers
|
516 |
|
|
AT91C_BASE_AC97C->AC97C_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;
|
517 |
|
|
|
518 |
|
|
// Clear channel transfers
|
519 |
|
|
for (channel = 0; channel < AC97C_CHANNEL_B_TRANSMIT; channel++) {
|
520 |
|
|
|
521 |
|
|
ac97c.transfers[channel].numSamples = 0;
|
522 |
|
|
}
|
523 |
|
|
}
|
524 |
|
|
|
525 |
|
|
//------------------------------------------------------------------------------
|
526 |
|
|
/// Configures the desired channel with the given value.
|
527 |
|
|
/// \param channel Channel number.
|
528 |
|
|
/// \param cfg Configuration value.
|
529 |
|
|
//------------------------------------------------------------------------------
|
530 |
|
|
void AC97C_ConfigureChannel(unsigned char channel, unsigned int cfg)
|
531 |
|
|
{
|
532 |
|
|
SANITY_CHECK((channel == AC97C_CHANNEL_A) || (channel == AC97C_CHANNEL_B));
|
533 |
|
|
|
534 |
|
|
if (channel == AC97C_CHANNEL_A) {
|
535 |
|
|
|
536 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR = cfg;
|
537 |
|
|
}
|
538 |
|
|
else {
|
539 |
|
|
|
540 |
|
|
AT91C_BASE_AC97C->AC97C_CBMR = cfg;
|
541 |
|
|
}
|
542 |
|
|
}
|
543 |
|
|
|
544 |
|
|
//------------------------------------------------------------------------------
|
545 |
|
|
/// Assigns the desired input slots to a particular channel.
|
546 |
|
|
/// \param channel Channel number (or 0 to unassign slots).
|
547 |
|
|
/// \param slots Bitfield value of slots to assign.
|
548 |
|
|
//------------------------------------------------------------------------------
|
549 |
|
|
void AC97C_AssignInputSlots(unsigned char channel, unsigned int slots)
|
550 |
|
|
{
|
551 |
|
|
unsigned int value;
|
552 |
|
|
unsigned int i;
|
553 |
|
|
|
554 |
|
|
SANITY_CHECK(channel <= AC97C_CHANNEL_B);
|
555 |
|
|
|
556 |
|
|
// Assign all slots
|
557 |
|
|
slots >>= 3;
|
558 |
|
|
for (i = 3; i < 15; i++) {
|
559 |
|
|
|
560 |
|
|
// Check if slots is selected
|
561 |
|
|
if (slots & 1) {
|
562 |
|
|
|
563 |
|
|
value = AT91C_BASE_AC97C->AC97C_ICA;
|
564 |
|
|
value &= ~(0x07 << ((i - 3) * 3));
|
565 |
|
|
value |= channel << ((i - 3) * 3);
|
566 |
|
|
AT91C_BASE_AC97C->AC97C_ICA = value;
|
567 |
|
|
}
|
568 |
|
|
slots >>= 1;
|
569 |
|
|
}
|
570 |
|
|
}
|
571 |
|
|
|
572 |
|
|
//------------------------------------------------------------------------------
|
573 |
|
|
/// Assigns the desired output slots to a particular channel.
|
574 |
|
|
/// \param channel Channel number (or 0 to unassign slots).
|
575 |
|
|
/// \param slots Bitfield value of slots to assign.
|
576 |
|
|
//------------------------------------------------------------------------------
|
577 |
|
|
void AC97C_AssignOutputSlots(unsigned char channel, unsigned int slots)
|
578 |
|
|
{
|
579 |
|
|
unsigned int value;
|
580 |
|
|
unsigned int i;
|
581 |
|
|
|
582 |
|
|
SANITY_CHECK(channel <= AC97C_CHANNEL_B);
|
583 |
|
|
|
584 |
|
|
// Assign all slots
|
585 |
|
|
slots >>= 3;
|
586 |
|
|
for (i = 3; i < 15; i++) {
|
587 |
|
|
|
588 |
|
|
// Check if slots is selected
|
589 |
|
|
if (slots & 1) {
|
590 |
|
|
|
591 |
|
|
value = AT91C_BASE_AC97C->AC97C_OCA;
|
592 |
|
|
value &= ~(0x07 << ((i - 3) * 3));
|
593 |
|
|
value |= channel << ((i - 3) * 3);
|
594 |
|
|
AT91C_BASE_AC97C->AC97C_OCA = value;
|
595 |
|
|
}
|
596 |
|
|
slots >>= 1;
|
597 |
|
|
}
|
598 |
|
|
}
|
599 |
|
|
|
600 |
|
|
//------------------------------------------------------------------------------
|
601 |
|
|
/// Returns 1 if no transfer is currently pending on the given channel;
|
602 |
|
|
/// otherwise, returns 0.
|
603 |
|
|
/// \param channel Channel number.
|
604 |
|
|
//------------------------------------------------------------------------------
|
605 |
|
|
unsigned char AC97C_IsFinished(unsigned char channel)
|
606 |
|
|
{
|
607 |
|
|
SANITY_CHECK(channel <= AC97C_CHANNEL_B_TRANSMIT);
|
608 |
|
|
|
609 |
|
|
if (ac97c.transfers[channel].numSamples > 0) {
|
610 |
|
|
|
611 |
|
|
return 0;
|
612 |
|
|
}
|
613 |
|
|
else {
|
614 |
|
|
|
615 |
|
|
return 1;
|
616 |
|
|
}
|
617 |
|
|
}
|
618 |
|
|
|
619 |
|
|
//------------------------------------------------------------------------------
|
620 |
|
|
/// Convenience function for synchronously sending commands to the codec.
|
621 |
|
|
/// \param address Register address.
|
622 |
|
|
/// \param data Command data.
|
623 |
|
|
//------------------------------------------------------------------------------
|
624 |
|
|
void AC97C_WriteCodec(unsigned char address, unsigned short data)
|
625 |
|
|
{
|
626 |
|
|
unsigned int sample;
|
627 |
|
|
|
628 |
|
|
sample = (address << 16) | data;
|
629 |
|
|
AC97C_Transfer(AC97C_CODEC_TRANSFER, (unsigned char *) &sample, 1, 0, 0);
|
630 |
|
|
while (!AC97C_IsFinished(AC97C_CODEC_TRANSFER));
|
631 |
|
|
}
|
632 |
|
|
|
633 |
|
|
//------------------------------------------------------------------------------
|
634 |
|
|
/// Convenience function for receiving data from the AC97 codec.
|
635 |
|
|
/// \param address Register address.
|
636 |
|
|
//------------------------------------------------------------------------------
|
637 |
|
|
unsigned short AC97C_ReadCodec(unsigned char address)
|
638 |
|
|
{
|
639 |
|
|
unsigned int sample;
|
640 |
|
|
|
641 |
|
|
sample = AT91C_AC97C_READ | (address << 16);
|
642 |
|
|
AC97C_Transfer(AC97C_CODEC_TRANSFER, (unsigned char *) &sample, 1, 0, 0);
|
643 |
|
|
while (!AC97C_IsFinished(AC97C_CODEC_TRANSFER));
|
644 |
|
|
|
645 |
|
|
return sample;
|
646 |
|
|
}
|
647 |
|
|
|
648 |
|
|
//------------------------------------------------------------------------------
|
649 |
|
|
/// Sets the size in bits of one sample on the given channel.
|
650 |
|
|
/// \param channel Channel number.
|
651 |
|
|
/// \param size Size of one sample in bits (10, 16, 18 or 24).
|
652 |
|
|
//------------------------------------------------------------------------------
|
653 |
|
|
void AC97C_SetChannelSize(unsigned char channel, unsigned char size)
|
654 |
|
|
{
|
655 |
|
|
unsigned int bits = 0;
|
656 |
|
|
|
657 |
|
|
SANITY_CHECK((size == 10) || (size == 16) || (size == 18) || (size == 24));
|
658 |
|
|
SANITY_CHECK((channel == AC97C_CHANNEL_A) || (channel == AC97C_CHANNEL_B));
|
659 |
|
|
|
660 |
|
|
switch (size) {
|
661 |
|
|
|
662 |
|
|
case 10 : bits = AT91C_AC97C_SIZE_10_BITS; break;
|
663 |
|
|
case 16 : bits = AT91C_AC97C_SIZE_16_BITS; break;
|
664 |
|
|
case 18 : bits = AT91C_AC97C_SIZE_18_BITS; break;
|
665 |
|
|
case 20 : bits = AT91C_AC97C_SIZE_20_BITS; break;
|
666 |
|
|
}
|
667 |
|
|
|
668 |
|
|
if (channel == AC97C_CHANNEL_A) {
|
669 |
|
|
|
670 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR &= ~(AT91C_AC97C_SIZE);
|
671 |
|
|
AT91C_BASE_AC97C->AC97C_CAMR |= bits;
|
672 |
|
|
}
|
673 |
|
|
else {
|
674 |
|
|
|
675 |
|
|
AT91C_BASE_AC97C->AC97C_CBMR &= ~(AT91C_AC97C_SIZE);
|
676 |
|
|
AT91C_BASE_AC97C->AC97C_CBMR |= bits;
|
677 |
|
|
}
|
678 |
|
|
}
|
679 |
|
|
|