nuaudma.c 9.62 KB
/*======================================================================*/
/*		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++;
}