/**************************************************************************//** * @file SPU.c * @version V3.00 * @brief N9H20 series SPU driver source file * * SPDX-License-Identifier: Apache-2.0 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ #include "string.h" #include "stdlib.h" #include "wblib.h" #include "N9H20_SPU.h" /* buffer */ UINT8 *_pucPlayAudioBuff; #if defined (__GNUC__) && !(__CC_ARM) __attribute__ ((aligned (256))) UINT8 playbuffer[FRAG_SIZE]; #else __align(256) UINT8 playbuffer[FRAG_SIZE]; #endif /* function declaration */ ERRCODE DrvSPU_InstallCallBack( E_DRVSPU_CHANNEL eChannel, UINT32 eIntSource, PFN_DRVSPU_INT_CALLBACK pfnCallback, PFN_DRVSPU_INT_CALLBACK *pfnOldCallback ); ERRCODE DrvSPU_Open(void); void DrvSPU_Close(void); void DrvSPU_ISR(void); ERRCODE DrvSPU_ChannelOpen(E_DRVSPU_CHANNEL eChannel); ERRCODE DrvSPU_ChannelClose(E_DRVSPU_CHANNEL eChannel); BOOL DrvSPU_IsChannelEnabled(E_DRVSPU_CHANNEL eChannel); ERRCODE DrvSPU_EnableInt(E_DRVSPU_CHANNEL eChannel, UINT32 eInt, PFN_DRVSPU_CB_FUNC* pfnCallBack); BOOL DrvSPU_IsIntEnabled(E_DRVSPU_CHANNEL eChannel, UINT32 eInt); ERRCODE DrvSPU_DisableInt(E_DRVSPU_CHANNEL eChannel, UINT32 eInt); ERRCODE DrvSPU_ClearInt(E_DRVSPU_CHANNEL eChannel, UINT32 eInt); ERRCODE DrvSPU_PollInt(E_DRVSPU_CHANNEL eChannel, UINT32 eInt); ERRCODE DrvSPU_SetBaseAddress(E_DRVSPU_CHANNEL eChannel, UINT32 u32Address); ERRCODE DrvSPU_SetThresholdAddress(E_DRVSPU_CHANNEL eChannel, UINT32 u32Address); ERRCODE DrvSPU_SetEndAddress(E_DRVSPU_CHANNEL eChannel, UINT32 u32Address); ERRCODE DrvSPU_SetPauseAddress(E_DRVSPU_CHANNEL eChannel, UINT32 u32Address); UINT32 DrvSPU_GetBaseAddress(UINT32 u32Channel); UINT32 DrvSPU_GetThresholdAddress(UINT32 u32Channel); UINT32 DrvSPU_GetCurrentAddress(UINT32 u32Channel); ERRCODE DrvSPU_GetLoopStartAddress(E_DRVSPU_CHANNEL eChannel, UINT32* pu32Address); UINT32 DrvSPU_GetEndAddress(UINT32 u32Channel); ERRCODE DrvSPU_GetPauseAddress(E_DRVSPU_CHANNEL eChannel, UINT32* pu32Address); ERRCODE DrvSPU_GetUserEventIndex(E_DRVSPU_CHANNEL eChannel, UINT8* pu8EventIndex, UINT8* pu8SubIndex); #ifdef OPT_DIRECT_SET_DFA ERRCODE DrvSPU_SetDFA(E_DRVSPU_CHANNEL eChannel, UINT16 u16DFA); #else ERRCODE DrvSPU_SetDFA(E_DRVSPU_CHANNEL eChannel, UINT16 u16SrcSampleRate, UINT16 u16OutputSampleRate); #endif ERRCODE DrvSPU_GetDFA(E_DRVSPU_CHANNEL eChannel, UINT16* pu16DFA); ERRCODE DrvSPU_SetPAN(E_DRVSPU_CHANNEL eChannel, UINT16 u16PAN); ERRCODE DrvSPU_GetPAN(E_DRVSPU_CHANNEL eChannel, UINT16* pu16PAN); ERRCODE DrvSPU_SetSrcType(E_DRVSPU_CHANNEL eChannel, E_DRVSPU_FORMAT eDataFormat); ERRCODE DrvSPU_GetSrcType(E_DRVSPU_CHANNEL eChannel, UINT16* pu16SrcType); ERRCODE DrvSPU_SetChannelVolume(E_DRVSPU_CHANNEL eChannel, UINT8 u8Volume); UINT8 DrvSPU_GetChannelVolume(UINT32 u32Channel); ERRCODE DrvSPU_SetChannelTone(E_DRVSPU_CHANNEL eChannel, S_TONE_CTRL* psToneCtrl); ERRCODE DrvSPU_GetChannelTone(E_DRVSPU_CHANNEL eChannel, S_TONE_CTRL* psToneCtrl); void DrvSPU_EqOpen(E_DRVSPU_EQ_BAND eEQBAND, E_DRVSPU_EQ_GAIN eEQGAIN); void DrvSPU_EqClose(void); void DrvSPU_SetVolume(UINT16 u16Volume); void DrvSPU_GetVolume(UINT16* pu16Volume); void DrvSPU_StartPlay(void); void DrvSPU_StopPlay(void); BOOL DrvSPU_IsSPUPlaying(void); UINT32 DrvSPU_SetSampleRate(UINT32 u32SystemClock, UINT32 SampleRate); ERRCODE DrvSPU_UploadChannelContents(E_DRVSPU_CHANNEL eChannel); ERRCODE DrvSPU_ChannelCtrl(S_CHANNEL_CTRL *psChannelCtrl); ERRCODE DrvSPU_ChannelPause(E_DRVSPU_CHANNEL eChannel); ERRCODE DrvSPU_ChannelResume(E_DRVSPU_CHANNEL eChannel); ERRCODE DrvSPU_SetToneAmp(E_DRVSPU_CHANNEL eChannel, UINT32 u32Amp); ERRCODE DrvSPU_SetTonePulse(E_DRVSPU_CHANNEL eChannel, UINT32 u32Pulse); UINT8 DrvSPU_ReadDACReg(UINT8 DACRegIndex); VOID DrvSPU_WriteDACReg(UINT8 DACRegIndex, UINT8 DACRegData); void DrvSPU_IntHandler(void); void SPU_SET_SAMPLE_RATE(UINT32 u32sysclk, UINT32 u32SampleRate) { DrvSPU_SetSampleRate(u32sysclk, u32SampleRate); } void SPU_SET_BASE_ADDRESS(UINT32 u32BaseAddress) { DrvSPU_SetBaseAddress(0, u32BaseAddress); DrvSPU_SetBaseAddress(1, u32BaseAddress); } UINT32 SPU_GET_BASE_ADDRESS(void) { return DrvSPU_GetBaseAddress(0); } void SPU_SET_TH1_ADDRESS(UINT32 u32TH1Address) { DrvSPU_SetThresholdAddress(0, u32TH1Address); DrvSPU_SetThresholdAddress(1, u32TH1Address); } UINT32 SPU_GET_TH1_ADDRESS(void) { return DrvSPU_GetThresholdAddress(0); } void SPU_SET_TH2_ADDRESS(UINT32 u32TH2Address) { DrvSPU_SetEndAddress(0, u32TH2Address); DrvSPU_SetEndAddress(1, u32TH2Address); } UINT32 SPU_GET_TH2_ADDRESS(void) { return DrvSPU_GetEndAddress(0); } UINT32 SPU_GET_CUR_ADDRESS(void) { return DrvSPU_GetCurrentAddress(0); } void SPU_STEREO(void) { DrvSPU_SetSrcType(0, 0x06); // Stereo PCM16 left DrvSPU_SetSrcType(1, 0x07); // Stereo PCM16 Right } void SPU_MONO(void) { DrvSPU_SetSrcType(0, 0x05); // Mono PCM16 } BOOL SPU_ISMONO(void) { return 0; } void SPU_SET_VOLUME(UINT16 u16CHRVolume, UINT16 u16CHLVolume) { UINT16 u16Voluem; // u16CHRVolume: 0x0(Mute) ~ 0x3F(Maximum) u16Voluem = (u16CHRVolume & 0x3F) | ((u16CHLVolume & 0x3F) << 8); DrvSPU_SetVolume(u16Voluem); } UINT8 SPU_GET_VOLUME(UINT8 u8Channel) { return DrvSPU_GetChannelVolume(u8Channel); } void SPU_SET_POWER_DOWN(UINT16 u16PowerDown) { outp32(REG_SPU_DAC_VOL, (inp32(REG_SPU_DAC_VOL) & ~ANA_PD) | ((u16PowerDown & 0xFFF)<<16)); } UINT16 SPU_GET_POWER_DOWN(void) { return (inp32(REG_SPU_DAC_VOL) & ~ANA_PD) >> 16; } UINT8 DacOnOffLevel; //must use this function before calling spuStartPlay() VOID spuDacOn(UINT8 level) { DacOnOffLevel = level; outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) | 0x30); //disable if(level == 3) outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) & ~0x30); //delay time, p0=3s else if(level == 1) outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) & ~0x20); //delay time, p0=0.5-1s else if(level == 2) outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) & ~0x10); //delay time, p0=2s else { outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) & ~0x03FF0000); //P7 outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) | 0x30); //disable return; } outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) & ~0x0800000); //P7 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) & ~0x0400000); //P6 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) & ~0x01e0000); //P1-4 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) & ~0x0200000); //P5 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) & ~0x00010000); //P0 if(level == 3) //modify this delay time to meet the product request sysDelay(220); else if(level == 1) sysDelay(70); else if(level == 2) sysDelay(30); } //must use this function after calling spuStopPlay() VOID spuDacOff(VOID) { sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) | 0x10000); //P0 if(DacOnOffLevel == 3) //modify this delay time to meet the product request sysDelay(150); else if(DacOnOffLevel == 1) sysDelay(70); else if(DacOnOffLevel == 2) sysDelay(40); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) | 0x200000); //P5 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) | 0x1e0000); //P1-4 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) | 0x400000); //P6 sysDelay(1); outp32(REG_SPU_DAC_VOL, inp32(REG_SPU_DAC_VOL) | 0x800000); //P7 sysDelay(1); outp32(REG_SPU_DAC_PAR, inp32(REG_SPU_DAC_PAR) | 0x30); //disable } VOID spuStartPlay(PFN_DRVSPU_CB_FUNC *fnCallBack, UINT8 *data) { DrvSPU_EnableInt(eDRVSPU_CHANNEL_0, DRVSPU_THADDRESS_INT, (PFN_DRVSPU_CB_FUNC*) fnCallBack); DrvSPU_EnableInt(eDRVSPU_CHANNEL_0, DRVSPU_ENDADDRESS_INT, (PFN_DRVSPU_CB_FUNC*) fnCallBack); memcpy(playbuffer, data, FRAG_SIZE); DrvSPU_StartPlay(); } VOID spuStopPlay(VOID) { int ii; for (ii=0; ii<32; ii++) { DrvSPU_DisableInt(ii, DRVSPU_ENDADDRESS_INT); DrvSPU_DisableInt(ii, DRVSPU_THADDRESS_INT); } DrvSPU_StopPlay(); sysDisableInterrupt(IRQ_SPU); } VOID spuIoctl(UINT32 cmd, UINT32 arg0, UINT32 arg1) { switch(cmd) { case SPU_IOCTL_SET_VOLUME: SPU_SET_VOLUME(arg0, arg1); break; case SPU_IOCTL_SET_MONO: SPU_MONO(); break; case SPU_IOCTL_SET_STEREO: SPU_STEREO(); break; case SPU_IOCTL_GET_FRAG_SIZE: *((UINT32 *)arg0) = FRAG_SIZE; break; default: break; } } VOID spuOpen(UINT32 u32SampleRate) { UINT32 u32SystemClock, u32ExtClock; _pucPlayAudioBuff = (UINT8 *)((UINT32)playbuffer | 0x80000000); memset(_pucPlayAudioBuff, 0, FRAG_SIZE); SPU_DISABLE(); // SPU must be disabled before to enable again //sysGetClockFreq(&pllcfg); u32ExtClock= sysGetExternalClock(); if (inp32(REG_CHIPCFG) & 0x01) u32SystemClock = sysGetPLLOutputKhz(eSYS_UPLL, u32ExtClock)*1000; else u32SystemClock = sysGetPLLOutputKhz(eSYS_UPLL, u32ExtClock)*1000; // 1.Check I/O pins. If I/O pins are used by other IPs, return error code. // 2.Enable IP��s clock // 3.Reset IP DrvSPU_Open(); DrvSPU_SetPAN(0, 0x1f1f); // Channel volume 0x4F for earphone and 0x3F for speaker DrvSPU_SetChannelVolume(0, 0x4F); // DrvSPU_SetChannelVolume(1, 0x4F); //0408 DrvSPU_ChannelOpen(0); DrvSPU_SetSampleRate(u32SystemClock, u32SampleRate); DrvSPU_SetSrcType(0, 0x06); // Stereo PCM16 left SPU_SET_BASE_ADDRESS((UINT32)_pucPlayAudioBuff); SPU_SET_TH1_ADDRESS((UINT32)_pucPlayAudioBuff + HALF_FRAG_SIZE); SPU_SET_TH2_ADDRESS((UINT32)_pucPlayAudioBuff + FRAG_SIZE); sysInstallISR(IRQ_LEVEL_1, IRQ_SPU, (PVOID)DrvSPU_IntHandler); sysSetLocalInterrupt(ENABLE_IRQ); sysEnableInterrupt(IRQ_SPU); } VOID spuClose (VOID) { sysDisableInterrupt(IRQ_SPU); DrvSPU_Close(); } VOID spuEqOpen (E_DRVSPU_EQ_BAND eEqBand, E_DRVSPU_EQ_GAIN eEqGain) { DrvSPU_EqOpen(eEqBand, eEqGain); } VOID spuEqClose (VOID) { DrvSPU_EqClose(); }