update DI disc code
[libogc.git] / libogc / audio.c
bloba74c35ecfa8371c6fdd935c4259423076270a956
1 /*-------------------------------------------------------------
3 audio.c -- Audio subsystem
5 Copyright (C) 2004
6 Michael Wiedenbauer (shagkur)
7 Dave Murphy (WinterMute)
9 This software is provided 'as-is', without any express or implied
10 warranty. In no event will the authors be held liable for any
11 damages arising from the use of this software.
13 Permission is granted to anyone to use this software for any
14 purpose, including commercial applications, and to alter it and
15 redistribute it freely, subject to the following restrictions:
17 1. The origin of this software must not be misrepresented; you
18 must not claim that you wrote the original software. If you use
19 this software in a product, an acknowledgment in the product
20 documentation would be appreciated but is not required.
22 2. Altered source versions must be plainly marked as such, and
23 must not be misrepresented as being the original software.
25 3. This notice may not be removed or altered from any source
26 distribution.
29 -------------------------------------------------------------*/
32 #include <stdlib.h>
33 #include "asm.h"
34 #include "processor.h"
35 #include "irq.h"
36 #include "audio.h"
37 #include "lwp_watchdog.h"
39 #define STACKSIZE 16384
41 // DSPCR bits
42 #define DSPCR_DSPRESET 0x0800 // Reset DSP
43 #define DSPCR_DSPDMA 0x0200 // ARAM dma in progress, if set
44 #define DSPCR_DSPINTMSK 0x0100 // * interrupt mask (RW)
45 #define DSPCR_DSPINT 0x0080 // * interrupt active (RWC)
46 #define DSPCR_ARINTMSK 0x0040
47 #define DSPCR_ARINT 0x0020
48 #define DSPCR_AIINTMSK 0x0010
49 #define DSPCR_AIINT 0x0008
50 #define DSPCR_HALT 0x0004 // halt DSP
51 #define DSPCR_PIINT 0x0002 // assert DSP PI interrupt
52 #define DSPCR_RES 0x0001 // reset DSP
54 #define _SHIFTL(v, s, w) \
55 ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s)))
56 #define _SHIFTR(v, s, w) \
57 ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1)))
59 #if defined(HW_DOL)
60 static vu32* const _aiReg = (u32*)0xCC006C00;
61 #elif defined(HW_RVL)
62 static vu32* const _aiReg = (u32*)0xCD006C00;
63 #else
64 #error HW model not supported.
65 #endif
67 static vu16* const _dspReg = (u16*)0xCC005000;
69 static u32 __AIInitFlag = 0;
70 static u32 __AIActive = 0;
71 static u8 *__CallbackStack = NULL;
72 static u8 *__OldStack = NULL;
74 static u64 bound_32KHz,bound_48KHz,min_wait,max_wait,buffer;
76 #if defined(HW_DOL)
77 static AISCallback __AIS_Callback;
78 #endif
79 static AIDCallback __AID_Callback;
81 static void __AICallbackStackSwitch(AIDCallback handler)
83 __asm__ __volatile__("mflr %r0\n\
84 stw %r0,4(%r1)\n\
85 stwu %r1,-24(%r1)\n\
86 stw %r31,20(%r1)\n\
87 mr %r31,%r3\n\
88 lis %r5,__OldStack@ha\n\
89 addi %r5,%r5,__OldStack@l\n\
90 stw %r1,0(%r5)\n\
91 lis %r5,__CallbackStack@ha\n\
92 addi %r5,%r5,__CallbackStack@l\n\
93 lwz %r1,0(%r5)\n\
94 subi %r1,%r1,8\n\
95 mtlr %r31\n\
96 blrl\n\
97 lis %r5,__OldStack@ha\n\
98 addi %r5,%r5,__OldStack@l\n\
99 lwz %r1,0(%r5)\n\
100 lwz %r0,28(%r1)\n\
101 lwz %r31,20(%r1)\n\
102 addi %r1,%r1,24\n\
103 mtlr %r0\n"
107 #if defined(HW_DOL)
108 static void __AISHandler(u32 nIrq,void *pCtx)
110 if(__AIS_Callback)
111 __AIS_Callback(_aiReg[2]);
112 _aiReg[0] |= 0x0008;
114 #endif
116 static void __AIDHandler(u32 nIrq,void *pCtx)
118 _dspReg[5] = (_dspReg[5]&~(DSPCR_DSPINT|DSPCR_ARINT))|DSPCR_AIINT;
119 if(__AID_Callback) {
120 if(!__AIActive) {
121 __AIActive = 1;
122 if(__CallbackStack)
123 __AICallbackStackSwitch(__AID_Callback);
124 else
125 __AID_Callback();
126 __AIActive = 0;
131 static void __AISRCINIT()
133 u64 tdiff;
134 u32 scnt,done = 0;
135 u64 time1,time2,wait;
137 while(!done) {
138 _aiReg[0] = (_aiReg[0]&~0x20)|0x20;
139 _aiReg[0] = (_aiReg[0]&~0x02);
140 _aiReg[0] = (_aiReg[0]&~0x01)|0x01;
142 scnt = _aiReg[2]&0x7fffffff;
143 while(scnt!=(_aiReg[2]&0x7fffffff));
145 time1 = gettime();
146 _aiReg[0] = (_aiReg[0]&~0x02)|0x02;
147 _aiReg[0] = (_aiReg[0]&~0x01)|0x01;
149 scnt = _aiReg[2]&0x7fffffff;
150 while(scnt!=(_aiReg[2]&0x7fffffff));
152 time2 = gettime();
153 _aiReg[0] = (_aiReg[0]&~0x02);
154 _aiReg[0] = (_aiReg[0]&~0x01);
156 tdiff = time2 - time1;
157 if(tdiff>(bound_32KHz - buffer) && tdiff<(bound_32KHz + buffer)) {
158 if(tdiff<(bound_48KHz - buffer)) {
159 wait = max_wait;
160 done = 1;
162 } else {
163 wait = min_wait;
164 done = 1;
168 while(diff_ticks(time2,gettime()) < wait);
171 #if defined(HW_DOL)
172 static void __AISetStreamSampleRate(u32 rate)
174 u32 currrate,level;
175 u32 playstate,volright,volleft,dsprate;
177 currrate = AUDIO_GetStreamSampleRate();
178 if(currrate!=rate) {
179 playstate = AUDIO_GetStreamPlayState();
180 volleft = AUDIO_GetStreamVolLeft();
181 volright = AUDIO_GetStreamVolRight();
182 AUDIO_SetStreamVolLeft(0);
183 AUDIO_SetStreamVolRight(0);
184 dsprate = _aiReg[0]&0x40;
185 _aiReg[0] = _aiReg[0]&~0x40;
187 _CPU_ISR_Disable(level);
188 __AISRCINIT();
189 _aiReg[0] |= dsprate;
190 _aiReg[0] = (_aiReg[0]&~0x20)|0x20;
191 _aiReg[0] = (_aiReg[0]&~0x02)|(_SHIFTL(rate,1,1));
192 _CPU_ISR_Restore(level);
194 AUDIO_SetStreamPlayState(playstate);
195 AUDIO_SetStreamVolLeft(volleft);
196 AUDIO_SetStreamVolRight(volright);
200 AISCallback AUDIO_RegisterStreamCallback(AISCallback callback)
202 u32 level;
204 AISCallback old = __AIS_Callback;
205 _CPU_ISR_Disable(level);
206 __AIS_Callback = callback;
207 _CPU_ISR_Restore(level);
208 return old;
210 #endif
212 void AUDIO_Init(u8 *stack)
214 u32 rate,level;
216 if(!__AIInitFlag) {
217 bound_32KHz = nanosecs_to_ticks(31524);
218 bound_48KHz = nanosecs_to_ticks(42024);
219 min_wait = nanosecs_to_ticks(42000);
220 max_wait = nanosecs_to_ticks(63000);
221 buffer = nanosecs_to_ticks(3000);
223 _aiReg[0] &= ~0x15;
224 _aiReg[1] = 0;
225 _aiReg[3] = 0;
227 _aiReg[0] = (_aiReg[0]&~0x20)|0x20;
229 rate = (_SHIFTR(_aiReg[0],6,1))^1;
230 if(rate==AI_SAMPLERATE_48KHZ) {
231 _aiReg[0] &= ~0x40;
232 _CPU_ISR_Disable(level);
233 __AISRCINIT();
234 _aiReg[0] |= 0x40;
235 _CPU_ISR_Restore(level);
238 __AID_Callback = NULL;
240 __OldStack = NULL; // davem - use it or lose it
241 // looks like 3.4 isn't picking up the use from the asm below
242 __CallbackStack = stack;
244 IRQ_Request(IRQ_DSP_AI,__AIDHandler,NULL);
245 __UnmaskIrq(IRQMASK(IRQ_DSP_AI));
246 #if defined(HW_DOL)
247 __AIS_Callback = NULL;
249 IRQ_Request(IRQ_AI,__AISHandler,NULL);
250 __UnmaskIrq(IRQMASK(IRQ_AI));
251 #endif
252 __AIInitFlag = 1;
256 #if defined(HW_DOL)
257 void AUDIO_SetStreamVolLeft(u8 vol)
259 _aiReg[1] = (_aiReg[1]&~0x000000ff)|(vol&0xff);
262 u8 AUDIO_GetStreamVolLeft()
264 return (u8)(_aiReg[1]&0xff);
267 void AUDIO_SetStreamVolRight(u8 vol)
269 _aiReg[1] = (_aiReg[1]&~0x0000ff00)|(_SHIFTL(vol,8,8));
272 u8 AUDIO_GetStreamVolRight()
274 return (u8)(_SHIFTR(_aiReg[1],8,8));
277 void AUDIO_SetStreamSampleRate(u32 rate)
279 _aiReg[0] = (_aiReg[0]&~0x0002)|(_SHIFTL(rate,1,1));
282 u32 AUDIO_GetStreamSampleRate()
284 return _SHIFTR(_aiReg[0],1,1);
287 void AUDIO_SetStreamTrigger(u32 cnt)
289 _aiReg[3] = cnt;
292 void AUDIO_ResetStreamSampleCnt()
294 _aiReg[0] = (_aiReg[0]&~0x20)|0x20;
297 void AUDIO_SetStreamPlayState(u32 state)
299 u32 playstate,streamrate;
300 u32 volright,volleft,level;
302 playstate = AUDIO_GetStreamPlayState();
303 streamrate = AUDIO_GetStreamSampleRate();
304 if(playstate!=state && state==AI_STREAM_START && streamrate==AI_SAMPLERATE_32KHZ ) {
305 volright = AUDIO_GetStreamVolRight();
306 AUDIO_SetStreamVolRight(0);
307 volleft = AUDIO_GetStreamVolLeft();
308 AUDIO_SetStreamVolLeft(0);
310 _CPU_ISR_Disable(level);
311 __AISRCINIT();
312 _aiReg[0] = (_aiReg[0]&~0x20)|0x20;
313 _aiReg[0] = (_aiReg[0]&~0x01)|0x01;
314 _CPU_ISR_Restore(level);
315 AUDIO_SetStreamVolRight(volright);
316 AUDIO_SetStreamVolLeft(volleft);
317 } else {
318 _aiReg[0] = (_aiReg[0]&~0x01)|(state&0x01);
322 u32 AUDIO_GetStreamPlayState()
324 return (_aiReg[0]&0x01);
326 #endif
328 AIDCallback AUDIO_RegisterDMACallback(AIDCallback callback)
330 u32 level;
331 AIDCallback old;
333 _CPU_ISR_Disable(level);
334 old = __AID_Callback;
335 __AID_Callback = callback;
336 _CPU_ISR_Restore(level);
337 return old;
340 void AUDIO_InitDMA(u32 startaddr,u32 len)
342 u32 level;
344 _CPU_ISR_Disable(level);
345 _dspReg[24] = (_dspReg[24]&~0x1fff)|(_SHIFTR(startaddr,16,13));
346 _dspReg[25] = (_dspReg[25]&~0xffe0)|(startaddr&0xffff);
347 _dspReg[27] = (_dspReg[27]&~0x7fff)|(_SHIFTR(len,5,15));
348 _CPU_ISR_Restore(level);
351 u16 AUDIO_GetDMAEnableFlag()
353 return (_SHIFTR(_dspReg[27],15,1));
356 void AUDIO_StartDMA()
358 _dspReg[27] = (_dspReg[27]&~0x8000)|0x8000;
361 void AUDIO_StopDMA()
363 _dspReg[27] = (_dspReg[27]&~0x8000);
366 u32 AUDIO_GetDMABytesLeft()
368 return (_SHIFTL(_dspReg[29],5,15));
371 u32 AUDIO_GetDMAStartAddr()
373 return (_SHIFTL((_dspReg[24]&0x1fff),16,13)|(_dspReg[25]&0xffe0));
376 u32 AUDIO_GetDMALength()
378 return ((_dspReg[27]&0x7fff)<<5);
381 void AUDIO_SetDSPSampleRate(u8 rate)
383 u32 level;
385 if(AUDIO_GetDSPSampleRate()!=rate) {
386 _aiReg[0] &= ~0x40;
387 if(rate==AI_SAMPLERATE_32KHZ) {
388 _CPU_ISR_Disable(level);
389 __AISRCINIT();
390 _aiReg[0] |= 0x40;
391 _CPU_ISR_Restore(level);
396 u32 AUDIO_GetDSPSampleRate()
398 return (_SHIFTR(_aiReg[0],6,1))^1; //0^1(1) = 48Khz, 1^1(0) = 32Khz