/*************************************************************************** * Copyright (c) 2003-2006, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "bsettop_pcm.h" #include "ministd.h" #include "bstd.h" #if (BCHP_CHIP!=7552) #include "brap.h" #include "brap_dspchn.h" #endif #include "gist.h" #include "cache_util.h" BDBG_MODULE(pcm); struct bpcm_play { unsigned index; pcm_play_state_t state; bdisplay_t display; bpcm_play_settings settings; #if (BCHP_CHIP!=7552) BRAP_ChannelHandle hRapPcmPbCh; BRAP_PcmBufInfo pcmBufInfo; #endif }; struct bpcm_play s_pcm[BPCM_PLAYBACK_CHANNELS] = { { 0, ePCM_PLAY_STATE_CLOSED, NULL, { /* bpcm_play_settings */ { 16, 48000, 2}, /* bpcm_settings */ NULL, NULL }, #if (BCHP_CHIP!=7552) NULL, { { NULL,NULL,NULL,NULL,0,0,0 }, { NULL,NULL,NULL,NULL,0,0,0 } } #endif } }; #if (BCHP_CHIP!=7552) extern void boutput_remove_decode_association(BRAP_ChannelHandle hRapCh); extern void boutput_add_decode_association(BRAP_ChannelHandle hRapCh); #endif static bresult b_pcm_rap_open(bpcm_play_t pcmplay ) { #if (BCHP_CHIP!=7552) BRAP_ChannelSettings chSettings; BERR_Code err = BERR_SUCCESS; /* Get default settings and parameters for this DEC channel */ err = BRAP_GetDefaultChannelSettings(GetRAP(), &chSettings); if (err != BERR_SUCCESS) { BDBG_ERR(("Get Decode Channel Default Settings failed for PCM PB Channel id %d", pcmplay->index)); return BERR_TRACE(err); } chSettings.eChType = BRAP_ChannelType_ePcmPlayback; chSettings.eChSubType = BRAP_ChannelSubType_eNone; /* OpenTimeMem testing */ chSettings.sChnRBufPool.uiMaxNumInChPairs[0]=0; chSettings.sChnRBufPool.uiMaxNumOutChPairs[0]=0; chSettings.sChnRBufPool.uiMaxNumRBuf=2; chSettings.sChnRBufPool.sBufSettings.pLeftBufferStart=NULL; chSettings.sChnRBufPool.sBufSettings.pRightBufferStart=NULL; chSettings.sChnRBufPool.sBufSettings.uiSize=0; chSettings.sChnRBufPool.sBufSettings.uiWaterMark=0; chSettings.bOpenTimeWrToRbuf = true; /* Open the RAP Decode Channel */ err = BRAP_OpenChannel(GetRAP(), &(pcmplay->hRapPcmPbCh), &(chSettings)); if (BERR_SUCCESS != err) { printf ("\n BRAP_Open_Channel Failure %x", err); return BERR_TRACE(err); } boutput_add_decode_association(pcmplay->hRapPcmPbCh); return err; #else return b_ok; #endif } static bresult b_pcm_rap_close(bpcm_play_t pcmplay ) { #if (BCHP_CHIP!=7552) BERR_Code err; boutput_remove_decode_association(pcmplay->hRapPcmPbCh); err = BRAP_CloseChannel(pcmplay->hRapPcmPbCh); pcmplay->hRapPcmPbCh = NULL; return err; #else return b_ok; #endif } static void b_pcm_stop(bpcm_play_t pcmplay) { #if (BCHP_CHIP!=7552) unsigned int flags; flags = bos_enter_critical(); if (pcmplay->hRapPcmPbCh) { BRAP_StopChannel(pcmplay->hRapPcmPbCh); } pcmplay->state = ePCM_PLAY_STATE_STOPPED; bos_exit_critical(flags); return; #endif } #if 0 static bool b_pcm_check_underflow(bpcm_play_t pcmplay, uint32_t read_offset) { return false; } /**************************************************************** * INPUTS: data - opaque data reference * OUTPUTS: None * RETURNS: None * FUNCTION: process audio interrupts ****************************************************************/ static void bpcm_isr(void *data) { //bpcm_play_t pcmplay = (bpcm_play_t)data; } #endif /* Summary: Open a PCM playback channel. */ bpcm_play_t bpcm_play_open( bobject_t id /* id corresponds to the pcm playback channel. */ ) { #if (BCHP_CHIP!=7552) unsigned int flags; bpcm_play_t pcmplay; if (id >= BPCM_PLAYBACK_CHANNELS) { BDBG_ERR(("%s: Invalid channel id specified (must be less than %d)!\n", __FUNCTION__, BPCM_PLAYBACK_CHANNELS)); return(bpcm_play_t)NULL; } pcmplay = &s_pcm[id]; flags = bos_enter_critical(); if (pcmplay->state > ePCM_PLAY_STATE_CLOSED) { bos_exit_critical(flags); BDBG_ERR(("PCM%d: Playback channel is already open!\n", id)); return(bpcm_play_t)NULL; } bos_exit_critical(flags); /* Check to see whether we have any playback channel already initialised */ flags = bos_enter_critical(); if (b_pcm_rap_open(pcmplay) == b_ok) { pcmplay->state = ePCM_PLAY_STATE_OPENED; } else pcmplay = NULL; bos_exit_critical(flags); return pcmplay; #else return NULL; #endif } /* Summary: Close a PCM playback channel. Description: Playback must already be stopped. */ void bpcm_play_close( bpcm_play_t pcmplay ) { unsigned int flags; BDBG_ASSERT(pcmplay); b_pcm_stop(pcmplay); flags = bos_enter_critical(); b_pcm_rap_close(pcmplay); pcmplay->state = ePCM_PLAY_STATE_CLOSED; bos_exit_critical(flags); } static bresult b_pcm_start( bpcm_play_t pcmplay) { #if (BCHP_CHIP!=7552) BERR_Code err; unsigned flags; BRAP_ChannelParams sChParams; flags = bos_enter_critical(); if (pcmplay->state == ePCM_PLAY_STATE_STARTED) { bos_exit_critical(flags); return BERR_INVALID_PARAMETER; } /* Get default parameters for a RAP Decode channel start operation */ err = BRAP_GetDefaultChannelParams(pcmplay->hRapPcmPbCh, &sChParams); BDBG_ASSERT(err == BERR_SUCCESS); /* Now store the user params properly into sPbChInfo[uiCurPbChId].sPbParams */ if (pcmplay->settings.pcm.bits_per_sample == 8) sChParams.eInputBitsPerSample = BRAP_InputBitsPerSample_e8; else if (pcmplay->settings.pcm.bits_per_sample == 16) sChParams.eInputBitsPerSample = BRAP_InputBitsPerSample_e16; if (pcmplay->settings.pcm.channels != 2) sChParams.eBufDataMode = BRAP_BufDataMode_eMono; else sChParams.eBufDataMode = BRAP_BufDataMode_eStereoInterleaved; sChParams.bLoopAroundEn = true; if (pcmplay->settings.pcm.sample_rate == 32000) sChParams.eInputSR = BAVC_AudioSamplingRate_e32k; else if (pcmplay->settings.pcm.sample_rate == 44100) sChParams.eInputSR = BAVC_AudioSamplingRate_e44_1k; else if (pcmplay->settings.pcm.sample_rate == 48000) sChParams.eInputSR = BAVC_AudioSamplingRate_e48k; sChParams.bPlayback = true; /* Start RAP Playback operation */ err = BRAP_StartChannel(pcmplay->hRapPcmPbCh, &sChParams); if(err != BERR_SUCCESS) { BDBG_ERR(("RAP Decode Channel %d Start failed", pcmplay->index)); bos_exit_critical(flags); return BERR_TRACE(err); } pcmplay->state = ePCM_PLAY_STATE_STARTED; bos_exit_critical(flags); #endif return b_ok; } /* Summary: Start playing PCM audio. */ bresult bpcm_play_start( bpcm_play_t pcmplay, bdisplay_t display, /* which output to play to. */ bpcm_play_settings *settings ) { #if (BCHP_CHIP!=7552) unsigned flags; BERR_Code err; BRAP_ChannelConfigParams sChConfigParams; BDBG_ASSERT(pcmplay); flags = bos_enter_critical(); if (pcmplay->state != ePCM_PLAY_STATE_OPENED) { bos_exit_critical(flags); return BERR_INVALID_PARAMETER; } memcpy(&(pcmplay->settings),settings,sizeof(bpcm_play_settings)); pcmplay->display = display; /* Get the default decoder configuration parameter */ err = BRAP_GetDefaultChannelConfig(pcmplay->hRapPcmPbCh, BRAP_DSPCHN_AudioType_ePcm, &sChConfigParams); BDBG_ASSERT(err == BERR_SUCCESS); sChConfigParams.uChConfigParams.sDecConfigParams.eOutputMode = BRAP_OutputMode_e2_0; sChConfigParams.uChConfigParams.sDecConfigParams.bOutputLfeOn = true; err = BRAP_SetChannelConfig(pcmplay->hRapPcmPbCh, &sChConfigParams); BDBG_ASSERT(err == BERR_SUCCESS); bos_exit_critical(flags); #endif return b_ok; } /* Summary: Get status information. */ bresult bpcm_play_get_status( bpcm_play_t pcmplay, bpcm_play_status *status /* [out] */ ) { #if (BCHP_CHIP!=7552) unsigned int flags; BERR_Code err; BRAP_PcmBufInfo pcmBufInfo; BDBG_ASSERT(pcmplay); BKNI_Memset(status,0,sizeof(bpcm_play_status)); flags = bos_enter_critical(); status->state = pcmplay->state; status->index = pcmplay->index; if ((pcmplay->state == ePCM_PLAY_STATE_OPENED) || (pcmplay->state == ePCM_PLAY_STATE_STARTED)) { /* Get the ring buffer info */ err = BRAP_PB_GetBufInfo(pcmplay->hRapPcmPbCh, &(pcmBufInfo)); if(err != BERR_SUCCESS) { bos_exit_critical(flags); return BERR_TRACE(err); } status->fifo_size = (uint32_t)pcmplay->pcmBufInfo.sLtOrSnglBufInfo.pEndPtr - (uint32_t)pcmplay->pcmBufInfo.sLtOrSnglBufInfo.pBasePtr; status->queued_bytes = status->fifo_size - pcmBufInfo.sLtOrSnglBufInfo.uiTotalSize ; status->buffer_base = pcmBufInfo.sLtOrSnglBufInfo.pBasePtr; } bos_exit_critical(flags); #endif return b_ok; } /** Summary: Get space available in the pcm playback buffer. Description: This is a non-destructive call. You can call it as many times as you want. After writing data into the buffer, you should call bpcm_play_write_complete to report how much of the buffer was used. **/ bresult bpcm_play_get_buffer( bpcm_play_t pcmplay, void **data, size_t *length ) { #if (BCHP_CHIP!=7552) unsigned int flags; BRAP_PcmBufInfo pcmBufInfo; BERR_Code err; BDBG_ASSERT(pcmplay); flags = bos_enter_critical(); *length = 0;*data = NULL; if ((pcmplay->state != ePCM_PLAY_STATE_STARTED) && (pcmplay->state != ePCM_PLAY_STATE_OPENED)) { bos_exit_critical(flags); return BERR_TRACE(BERR_INVALID_PARAMETER); } else { /* Get the ring buffer info */ err = BRAP_PB_GetBufInfo(pcmplay->hRapPcmPbCh, &(pcmBufInfo)); if(err != BERR_SUCCESS) { bos_exit_critical(flags); return BERR_TRACE(err); } *data = pcmBufInfo.sLtOrSnglBufInfo.pWritePtr; *length = pcmBufInfo.sLtOrSnglBufInfo.uiContiguousSize; } bos_exit_critical(flags); #endif return b_ok; } /** Summary: **/ bresult bpcm_play_write_complete( bpcm_play_t pcmplay, size_t amount_written ) { #if (BCHP_CHIP!=7552) unsigned int flags; BRAP_PcmBufSzUpdt pcmBufSzUpdt; BERR_Code err; BDBG_ASSERT(pcmplay); flush_dcache((uint32_t)pcmplay->pcmBufInfo.sLtOrSnglBufInfo.pWritePtr, (uint32_t)pcmplay->pcmBufInfo.sLtOrSnglBufInfo.pWritePtr + amount_written); flags = bos_enter_critical(); pcmBufSzUpdt.uiLtOrSnglBufSzUpdt = amount_written; pcmBufSzUpdt.uiRtBufSzUpdt = 0; err = BRAP_PB_UpdateBufUsg(pcmplay->hRapPcmPbCh,&pcmBufSzUpdt); err = BRAP_PB_BufferWriteDone(pcmplay->hRapPcmPbCh); bos_exit_critical(flags); /* we do a started check within that function */ if (pcmplay->state == ePCM_PLAY_STATE_OPENED) { b_pcm_start(pcmplay); } #endif return b_ok; } /* Summary: Stop PCM playback */ bresult bpcm_play_stop( bpcm_play_t pcmplay ) { BDBG_ASSERT(BPCM_PLAYBACK_CHANNELS); b_pcm_stop(pcmplay); return b_ok; } static void b_pcm_nocallback(void *context) { return; } void bpcm_play_settings_init(bpcm_play_settings *settings, bpcm_play_t pcmplay) { settings->pcm.bits_per_sample = 16; settings->pcm.sample_rate = 48000; settings->pcm.channels = 2; settings->callback_context = NULL; settings->callback = b_pcm_nocallback; return; } #ifdef CONFIG_PCM_TEST #include "bsettop.h" #include "bsettop_hdmi.h" void square_wave(char* ptr,int samples) { int i; for (i = 0; i < samples; i +=8) { ptr[i] = 0xF1; ptr[i+1] = 0xF1; ptr[i+2] = 0xF1; ptr[i+3] = 0xF1; ptr[i+4] = 0; ptr[i+5] = 0; ptr[i+6] = 0; ptr[i+7] = 0; } } void bpcm_play_test(void) { bpcm_play_settings settings; bpcm_play_t pcmplay; size_t length; void *write_ptr; bdisplay_t display; bsettop_hdmi_t hdmi; bresult rc; int cnt = 0xFFFF; bsettop_init(0); display = bdisplay_open(0); BDBG_ASSERT(display); rc = bsettop_hdmi_open(&hdmi,display); BDBG_ASSERT(rc == b_ok); pcmplay = bpcm_play_open(0); BDBG_ASSERT(pcmplay); bpcm_play_settings_init(&settings,pcmplay); settings.pcm.bits_per_sample = 8; settings.pcm.sample_rate = 48000; settings.pcm.channels = 1; /* only works for 1 channel 8 bps */ bpcm_play_start(pcmplay,NULL,&settings); while (cnt--) { if (bpcm_play_get_buffer(pcmplay,&write_ptr,&length) == b_ok) { square_wave((char*)write_ptr,length); bpcm_play_write_complete(pcmplay,length); } else { BDBG_WRN(("bpcm_play_get_buffer failed %d",cnt)); } } return; } #endif