nuaudma.c
9.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*======================================================================*/
/* NuSystem */
/* nuaudma.c */
/* */
/* Copyright (C) 1998, NINTENDO Co,Ltd. */
/* */
/* 98/12/09 Created by K.Ohki(SLANP) */
/*======================================================================*/
/* $Id: nuaudma.c,v 1.1.1.1 2002/10/30 02:07:09 blythe Exp $ */
/*======================================================================*/
#include <nusys.h>
#include <nualsgi.h>
OSMesgQueue nuAuDmaMesgQ;
OSMesg* nuAuDmaMesgBuf;
OSIoMesg* nuAuDmaIOMesgBuf;
NUDMAState nuAuDmaState;
NUDMABuffer* nuAuDmaBuf;
s16 nuAuDmaBufNum = NU_AU_DMA_BUFFER_NUM;
s16 nuAuDmaBufSize = NU_AU_DMA_BUFFER_SIZE;
static s32 nuAuDmaNext;
/*----------------------------------------------------------------------*/
/* DMA CallBack */
/* */
/* シンセサイズドライバのDMAコールバックルーチン */
/* DMABufferに必要な値があれば、そのバッファのポインタを返し、 */
/* なければ、データをDMA転送してバッファに読み込む。 */
/* この仕組みによりDMAコールの回数を減らすようにしている。 */
/* 使用されない部分は2フレームで消去される。 */
/* IN addr データのあるROMのアドレス */
/* len データサイズ */
/*----------------------------------------------------------------------*/
s32 nuAuDmaCallBack(s32 addr, s32 len, void *state)
{
void* freeBuffer;
int delta;
NUDMABuffer* dmaPtr;
NUDMABuffer* lastDmaPtr;
s32 addrEnd,buffEnd;
OSIoMesg* dmaIoMesgBufPtr;
#ifdef NU_DEBUG
nuAuDebDmaCallbackFrame++;
if(len > nuAuDmaBufSize){
nuAuDebStatus |= NU_AU_DEBUG_DMABUFSIZE;
if(nuAuDebFlag & NU_AU_DEBUG_NORMAL){
osSyncPrintf("nuAuDmaCallBack: DMA Buffer size is too small.\n");
}
}
#endif /* NU_DEBUG */
lastDmaPtr = NULL;
dmaPtr = nuAuDmaState.firstUsed;
addrEnd = addr+len;
/* バッファに欲しいデータがあるかチェック */
while(dmaPtr) {
buffEnd = dmaPtr->startAddr + nuAuDmaBufSize;
/*--------------------------------------*/
/* DMABufferはstartAddrが昇順に整列され*/
/* ているので、小さい値がきたら */
/* バッファに欲しいデータはない */
/*--------------------------------------*/
if(dmaPtr->startAddr > addr) { /* バッファにデータが無い */
break;
} else if(addrEnd <= buffEnd){ /* バッファにデータが存在する */
/* フレームカウンタの値を再度セット */
dmaPtr->frameCnt = NU_AU_BUFFER_USE_FRAME;
/* 実際に必要なデータがあるアドレスの計算 */
freeBuffer = dmaPtr->ptr + addr - dmaPtr->startAddr;
/* バッファの物理アドレスを返す */
return (s32) osVirtualToPhysical(freeBuffer);
}
lastDmaPtr = dmaPtr;
dmaPtr = (NUDMABuffer*)dmaPtr->node.next;
}
/*------------------------------------------------------------------*/
/* DMABufferに欲しいデータが含まれていなかったので、データをDMAで */
/* 転送するよう設定する */
/*------------------------------------------------------------------*/
/* DMABufferのうち空きバッファを1つ取り出す */
dmaPtr = nuAuDmaState.firstFree;
/* 空きバッファが無い場合にはNULLが取り出される */
/* ひとまず使用中のバッファの先頭のポインタを返す */
/* 止まるよりはましなので */
if(dmaPtr == NULL){
#ifdef NU_DEBUG
if(nuAuDebFlag & NU_AU_DEBUG_NORMAL){
osSyncPrintf("auDmaCallBack: no dma buffer!!\n");
}
nuAuDebStatus |= NU_AU_DEBUG_NODMABUF;
#endif /* NU_DEBUG */
return(int)OS_K0_TO_PHYSICAL(nuAuDmaState.firstUsed->ptr);
}
nuAuDmaState.firstFree = (NUDMABuffer*)dmaPtr->node.next;
alUnlink((ALLink*)dmaPtr);
/*--------------------------------------------------*/
/* 取得したDMABufferを使用中のリンクリストに */
/* startAddrが昇順になるように挿入する */
/*--------------------------------------------------*/
if(lastDmaPtr) {
/* 普通にリンクリストに挿入 */
alLink((ALLink*)dmaPtr,(ALLink*)lastDmaPtr);
} else if(nuAuDmaState.firstUsed) {
/* リンクリストの先頭に挿入 */
lastDmaPtr = nuAuDmaState.firstUsed;
nuAuDmaState.firstUsed = dmaPtr;
dmaPtr->node.next = (ALLink*)lastDmaPtr;
dmaPtr->node.prev = 0;
lastDmaPtr->node.prev = (ALLink*)dmaPtr;
} else {
/* 使用中のDMABufferが存在しない場合 */
nuAuDmaState.firstUsed = dmaPtr;
dmaPtr->node.next = 0;
dmaPtr->node.prev = 0;
}
#ifdef USE_EPI
dmaIoMesgBufPtr = &nuAuDmaIOMesgBuf[nuAuDmaNext++];
freeBuffer = dmaPtr->ptr;
delta = addr & 0x1;
addr -= delta;
dmaPtr->startAddr = addr; /* DMA用にアドレス位置を2バイト境界にする */
dmaPtr->frameCnt = NU_AU_BUFFER_USE_FRAME;
#ifdef NU_DEBUG
nuAuDebDmaFrame++;
if(nuAuDebFlag & NU_AU_DEBUG_DISABLEDMA){
return (s32)osVirtualToPhysical(dmaPtr->ptr); /* dummy */
}
#endif /* NU_DEBUG */
dmaIoMesgBufPtr->hdr.pri = OS_MESG_PRI_NORMAL;
dmaIoMesgBufPtr->hdr.retQueue = &nuAuDmaMesgQ;
dmaIoMesgBufPtr->dramAddr = freeBuffer;
dmaIoMesgBufPtr->devAddr = (u32)addr;
dmaIoMesgBufPtr->size = nuAuDmaBufSize;
osEPiStartDma(nuPiCartHandle, dmaIoMesgBufPtr, OS_READ);
#else
freeBuffer = dmaPtr->ptr;
delta = addr & 0x1;
addr -= delta; /* DMA用にアドレス位置を2バイトに境界にする */
dmaPtr->startAddr = addr;
dmaPtr->frameCnt = NU_AU_BUFFER_USE_FRAME;
#ifdef NU_DEBUG
nuAuDebDmaFrame++;
if(nuAuDebFlag & NU_AU_DEBUG_DISABLEDMA){
return (s32)osVirtualToPhysical(dmaPtr->ptr); /* dummy */
}
#endif /* NU_DEBUG */
/* DMA転送 */
osPiStartDma(&nuAuDmaIOMesgBuf[nuAuDmaNext++], OS_MESG_PRI_NORMAL, OS_READ,
(u32)addr, freeBuffer, nuAuDmaBufSize, &nuAuDmaMesgQ);
#endif /* USE_EPI */
return (s32) osVirtualToPhysical(freeBuffer) + delta;
}
/*----------------------------------------------------------------------*/
/* auDmaNew - sequence player DMA callback initialize */
/* */
/* シーケンスプレイヤーに登録したとき初期用に1度呼び出される */
/* コールバックルーチン。次からはreturnで返したコールバックルーチン */
/* が呼び出される。 */
/* IN **state ステータスのポインタ */
/* ここにポインタを設定しておくとDMAcallback時のstateに */
/* ポインタが渡される */
/*----------------------------------------------------------------------*/
ALDMAproc nuAuDmaNew(NUDMAState **state)
{
s32 i;
/* 初期化されているかチェック。初期化されいない場合は初期化する */
if(!nuAuDmaState.initialized) {
nuAuDmaMesgBuf = nuAuHeapAlloc(nuAuDmaBufNum * sizeof(OSMesg));
nuAuDmaIOMesgBuf = nuAuHeapAlloc(nuAuDmaBufNum * sizeof(OSIoMesg));
/* DMAコールバック用のメッセージキューの作成 */
osCreateMesgQueue(&nuAuDmaMesgQ, nuAuDmaMesgBuf, nuAuDmaBufNum);
/* DMAバッファのポインタ配列用のバッファの確保 */
nuAuDmaBuf = nuAuHeapAlloc(nuAuDmaBufNum * sizeof(NUDMABuffer));
nuAuDmaState.firstFree = nuAuDmaBuf; /* 空きDMABufリストの先頭を登録 */
nuAuDmaState.firstUsed = NULL; /* 使用中DMABufferなし */
nuAuDmaBuf[0].node.prev = NULL;
nuAuDmaBuf[0].node.next = NULL;
/* ポインタのリンク作成 */
for (i = 0; i < nuAuDmaBufNum - 1; i++){
alLink((ALLink*)&nuAuDmaBuf[i+1], (ALLink*)&nuAuDmaBuf[i]);
nuAuDmaBuf[i].ptr = nuAuHeapAlloc(nuAuDmaBufSize);
}
nuAuDmaBuf[i].ptr = nuAuHeapAlloc(nuAuDmaBufSize);
/* 初期化済みフラグセット */
nuAuDmaState.initialized++;
}
nuAuDmaNext = 0;
*state = &nuAuDmaState; /* nuDmaState構造体のポインタをstateにセット*/
return nuAuDmaCallBack;
}
/*----------------------------------------------------------------------*/
/* auCleanDMABuffers - DMA bufferリストのクリア */
/* */
/* DMABufferをサーチして2フレーム以上使われていない */
/* バッファを空きバッファのリンクリストに追加する。 */
/* */
/* IN 無し */
/* RTN 無し */
/*----------------------------------------------------------------------*/
void nuAuCleanDMABuffers(void)
{
NUDMABuffer *dmaPtr,*nextPtr;
#ifdef NU_DEBUG
u32 DmaUseCnt = 0;
#endif /* NU_DEBUG */
#ifdef NU_DEBUG
while(nuAuDmaNext){
if(osRecvMesg(&nuAuDmaMesgQ, NULL, OS_MESG_NOBLOCK)){
if(nuAuDebFlag & NU_AU_DEBUG_NORMAL){
osSyncPrintf("nuAuCleanDMABuffers: Audio DMA not completed!!\n");
}
nuAuDebStatus |= NU_AU_DEBUG_DMANOTCOMPLETE;
}
nuAuDmaNext--;
}
#else
nuAuDmaNext = 0;
#endif /* NU_DEBUG */
dmaPtr =nuAuDmaState.firstUsed;
while(dmaPtr) {
nextPtr = (NUDMABuffer*)dmaPtr->node.next;
/*------------------------------------------------------*/
/* NU_AU_BUFFER_USE_FRAME数以上経過したバッファを */
/* 空きバッファのリンクリストへ追加する。 */
/* NU_AU_BUFFER_USE_FRAME数を増やすにはバッファを増やす*/
/* 必要があり、減らすとDMAの使用回数が増える。痛し痒し */
/*------------------------------------------------------*/
if(!dmaPtr->frameCnt--){
if(nuAuDmaState.firstUsed == dmaPtr){
nuAuDmaState.firstUsed = (NUDMABuffer*)dmaPtr->node.next;
}
alUnlink((ALLink*)dmaPtr);
if(nuAuDmaState.firstFree){
alLink((ALLink*)dmaPtr,(ALLink*)nuAuDmaState.firstFree);
}else{
nuAuDmaState.firstFree = dmaPtr;
dmaPtr->node.next = 0;
dmaPtr->node.prev = 0;
}
}
#ifdef NU_DEBUG
else {
DmaUseCnt++;
}
#endif /* NU_DEBUG */
dmaPtr = nextPtr;
}
#ifdef NU_DEBUG
/* DMAバッファ使用最大値の更新 */
if(nuAuDebDmaBufMaxUse < DmaUseCnt){
nuAuDebDmaBufMaxUse = DmaUseCnt;
}
if(nuAuDebDmaCallbackMax < nuAuDebDmaCallbackFrame){
nuAuDebDmaCallbackMax = nuAuDebDmaCallbackFrame;
}
if(nuAuDebDmaMax < nuAuDebDmaFrame){
nuAuDebDmaMax = nuAuDebDmaFrame;
}
nuAuDebDmaCount += nuAuDebDmaFrame;
nuAuDebDmaCallbackCount += nuAuDebDmaCallbackFrame;
nuAuDebDmaFrame = 0;
nuAuDebDmaCallbackFrame = 0;
#endif /* NU_DEBUG */
nuAuFrameCounter++;
}