/* * audioSample_io.c * * This file contains the test / demo code to demonstrate the Audio component * driver functionality on SYS/BIOS 6. * * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** \file audioSample_io.c * * \brief sample application for demostration of audio playing * * This file contains the implementation of the sample appliation for the * demonstration of audio playing through the audio interface layer. * * (C) Copyright 2017, Texas Instruments, Inc */ /* ========================================================================== */ /* INCLUDE FILES */ /* ========================================================================== */ #include "fil.h" /* FILE I/O implementation */ #include "sys.h" /* System API and structures */ #include "sysbfflt.h" /* System support for BF filters */ #include "cmb.h" #include #include #include "../../../common/components/mss/mss.h" /* local version used */ #include #if (SYS_USE_DRC) ///#include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mcasp_osal.h" #include "ICodec.h" #include "mcasp_cfg.h" #include "MCASP_log.h" #include "stdio.h" #include "string.h" #include #ifdef MEASURE_TIME #include "profiling.h" #endif #include #include #include /* for System_printf, and similar */ #include /* for benchmarking/profiling */ #include "pcm186x_if.h" /* ========================================================================== */ /* IMPORTED VARIABLES */ /* ========================================================================== */ extern EDMA3_DRV_Handle hEdma; extern HeapMem_Handle myHeap; /* ========================================================================== */ /* MACRO DEFINITIONS */ /* ========================================================================== */ #define Timestamp_get Timestamp_get32 /* use 32-bit time stamps */ #define MIN(a,b) (((a)>(b))?(b):(a)) /* Min/Max macros */ #define MAX(a,b) (((a)<(b))?(b):(a)) #define loop while(1) /* endless loop for the task */ /* * Buffers placed in external memory are aligned on a 128 bytes boundary. * In addition, the buffer should be of a size multiple of 128 bytes for * the cache work optimally on the C6x. */ #define BUFLEN 160*2 /* number of samples in the frame */ #define BUFALIGN 128 /* alignment of buffer for use of L2 cache */ /** Number of serializers configured for record */ #define BUFSIZE (BUFLEN * 4) // 4 bytes per word /* This is the number of buffers used by the application to be issued and reclaimed This number can be higher than 2 (Ping pong) also. The McASP driver puts them in a queue internally and process them in order and give back to the application */ #define NUM_BUFS 2 #if defined(AIC_CODEC) #include Ptr hAicDev; Ptr hAicChannel; #endif /* Function prototype */ static Void createStreams(); static Void prime(); Ptr rxbuf[NUM_BUFS]; Ptr txbuf[NUM_BUFS]; Ptr wkbuf[NUM_BUFS]; Ptr outbuf[NUM_BUFS]; /* channel 0 (serilizer 1 left) - mic1 */ /* channel 1 (serilizer 1 right) - mic2 */ /* channel 2 (serilizer 2 left) - mic5 */ /* channel 3 (serilizer 2 right) - mic6 */ /* channel 4 (serilizer 3 left) - mic3 */ /* channel 5 (serilizer 3 right) - mic4 */ /* channel 6 (serilizer 4 left) - mic8 */ /* channel 7 (serilizer 4 right) - mic7 */ int chanToMicMapping[8] = {0, 4, 3, 7, 1, 5, 2, 6}; /* McASP Device handles */ Ptr hMcaspDev0; Ptr hMcaspDev; /* McASP Device parameters */ Mcasp_Params mcaspParams; Mcasp_Params mcaspParams0; /* Channel Handles */ Ptr hMcaspTxChan; Ptr hMcaspRxChan; Ptr hMcaspTxChan0; Ptr hMcaspRxChan0; int rxFrameIndex=(NUM_BUFS-1), txFrameIndex=(NUM_BUFS-1); volatile int RxFlag=0,TxFlag=0; Semaphore_Handle semR,semT; Semaphore_Params params; Error_Block eb; typedef struct txBfDebug_stc { tulong frmcnt; /* normal frames */ tulong silcnt; /* silence frames */ tuint invsrc; /* no mic active, invalid output */ tuint invopt; /* >1 mic active, invalid output */ } txBfDebug_t; typedef struct txTaskDebug_stc { tuint overrun; /* counts how many times we ran out of MIPS */ txBfDebug_t bf[SYS_VMICS_MAX]; /* beamformer statistics */ } txTaskDebug_t; txTaskDebug_t txTaskDebug; /* Tx task debug stats */ /* Profiling/benchmarking information for the Tx task */ typedef struct txTaskProfileData_stc { tulong min; /* Minimum number of cycles */ tulong max; /* Maximum number of cycles */ tulong n; /* Number of measurements */ float total; /* Total number of cycles */ } txTaskProfileData_t; typedef struct txTaskProfile_stc { txTaskProfileData_t bf; /* Beamformer profile */ txTaskProfileData_t asnr; /* ASNR profile */ txTaskProfileData_t mss; /* MSS profile */ txTaskProfileData_t drc; /* DRC profile */ txTaskProfileData_t vau; /* VAU profile */ } txTaskProfile_t; volatile txTaskProfile_t txTaskProfile = { {~(0uL), 0, 0, 0.0f}, {~(0uL), 0, 0, 0.0f}, {~(0uL), 0, 0, 0.0f}, {~(0uL), 0, 0, 0.0f}, {~(0uL), 0, 0, 0.0f} }; /* To be used for debug trace */ mssSrc_t mssDbgCurSrc = { -1, -1 /* Current source group/index */ }; mssSrc_t mssDbgNewSrc = { -1, -1 /* New source group/index */ }; #if (SYS_USE_DRC) /* Output frame for MSS, input for DRC */ #pragma DATA_ALIGN(txOutFrame1,8) linSample txOutFrame1[SYS_FRAME_LENGTH]; /* Output frame for DRC, input for VAU */ #pragma DATA_ALIGN(txOutFrame2,8) linSample txOutFrame2[SYS_FRAME_LENGTH]; #endif /**************************************************************************************/ /* FUNCTION DESCRIPTION: This utility function converts local GEM L2 address in to global memory addresses used by the EDMA inside McASP */ /**************************************************************************************/ static uint32_t getGlobalAddr (uint32_t addr) { if ((addr >= 0x800000) && (addr < 0x1000000)) { #ifdef _TMS320C6X uint32_t coreNum; /* Get the core number. */ coreNum = CSL_chipReadReg(CSL_CHIP_DNUM); #if defined(SOC_AM572x) || defined(SOC_AM571x) /* Compute the global address. */ return ((1 << 30) | (coreNum << 24) | (addr & 0x00ffffff)); #else /* Compute the global address. */ return ((1 << 28) | (coreNum << 24) | (addr & 0x00ffffff)); #endif #else return addr; #endif } else { /* non-L2 address range */ return addr; } } /*********************** APPLICATION DEFINED FUNCTIONS: Begin ****************************/ /* The below functions need to be defined by the application and are registered to the McASP driver during instantiation */ /* * This call back function is provided to the McASP driver during mcaspCreateChan() * and is called at the end of a transaction. This example uses the same call back function * for both TX and RX transfers and the call back argument is not being used in this * application and hence we pass NULL during mcaspCreateChan() as the call back argument. * This need not be the case for other applications where they could use a seperate * call back function for TX and RX. Also they could provide a non-NULL argument as * the call back argument and use it in their implementation of the call back function(s). */ void mcaspAppCallback(void* arg, MCASP_Packet *ioBuf) { if(ioBuf->cmd == MCASP_READ) { RxFlag++; rxFrameIndex=((rxFrameIndex+1) %NUM_BUFS); if(ioBuf->addr != (void *)getGlobalAddr((uint32_t)rxbuf[rxFrameIndex])) { MCASP_log("Rx Buf Address mismatch\n"); } /* post semaphore */ Semaphore_post(semR); } if(ioBuf->cmd == MCASP_WRITE) { txFrameIndex=((txFrameIndex+1) % NUM_BUFS); if(ioBuf->addr != (void *)getGlobalAddr((uint32_t)txbuf[txFrameIndex])) { MCASP_log("Tx Buf Address mismatch\n"); } TxFlag++; /* post semaphore */ Semaphore_post(semT); } } /* * This call back is used during interrupt processing and is defined by the * application for error handling. These functions are called back from within the * mcasp driver when an error interrupt happens and macspIsr() is being called. * The sample error handling functions just records these errors which * are later used for analyzing the errors seen. */ /* The below variables are used to quit the frame processing loop if an error occurs */ int gblErrFlagXmt=0; int gblErrFlagRcv=0; /* The below variables are used to analyze the errors if an error interrupt happens */ Mcasp_errCbStatus errCbStatusXmt; Mcasp_errCbStatus errCbStatusRcv; /* Error handler for Transmit side */ void GblErrXmt(Mcasp_errCbStatus errCbStat) { gblErrFlagXmt=1; errCbStatusXmt=errCbStat; } /* Error handler for Rcv side */ void GblErrRcv(Mcasp_errCbStatus errCbStat) { gblErrFlagRcv=1; errCbStatusRcv=errCbStat; } /*********************** APPLICATION DEFINED FUNCTIONS: End ****************************/ /**************************************************************************************/ /* FUNCTION DESCRIPTION: This function analyzes the result of error interrupts, if it * happened */ /**************************************************************************************/ void mcaspAnalyzeErrors(Mcasp_errCbStatus *errCbStat) { MCASP_log("***** --------- Error Interrupt details \n -----------"); MCASP_log("***** Error Callback:isClkFailErr : %d\n",errCbStat->isClkFailErr); MCASP_log("***** Error Callback: isDMAErr : %d\n",errCbStat->isDMAErr); MCASP_log("***** Error Callback: isSyncErr : %d\n",errCbStat->isSyncErr); MCASP_log("***** Error Callback: retVal : %d \n",errCbStat->retVal); MCASP_log("***** Error Callback: isRcvOvrRunOrTxUndRunErr : %d \n",errCbStat->isRcvOvrRunOrTxUndRunErr); } /**************************************************************************************/ /* FUNCTION DESCRIPTION: This function creates the McASP channels for Tx and Rx This function also creates the codec channels (if any) */ /**************************************************************************************/ static Void createStreams() { int status; int mode = IOM_INPUT; char remName[10]="aic"; #if !defined(MCASP_MASTER) /* Configure the external clock: In Slave mode, McASP is not the master, start initializing the external clock provider (AIC codec below), before configuring McASP clocks (in mcaspCreateChan() below) */ #if defined(AIC_CODEC) /* In this case AIC provides the frame clocks, hence we need to start it first */ status = aic31MdCreateChan( &hAicChannel, hAicDev, remName, mode, (Ptr)(&AIC31_config), mcaspAppCallback, NULL); if ((NULL == hAicChannel) && (IOM_COMPLETED != status)) { MCASP_log("AIC Create Channel Failed\n"); BIOS_exit(0); } #endif #endif mcasp_chanparam[0].edmaHandle = hEdma; mcasp_chanparam[1].edmaHandle = hEdma; /* Create Mcasp channel for Tx */ status = mcaspCreateChan(&hMcaspTxChan, hMcaspDev, MCASP_OUTPUT, &mcasp_chanparam[1], mcaspAppCallback, NULL); if((status != MCASP_COMPLETED) || (hMcaspTxChan == NULL)) { MCASP_log("mcaspCreateChan for McASP1 Tx Failed\n"); BIOS_exit(0); } /* Create Mcasp channel for Rx */ status = mcaspCreateChan(&hMcaspRxChan, hMcaspDev, MCASP_INPUT, &mcasp_chanparam[0], mcaspAppCallback, NULL); if((status != MCASP_COMPLETED) || (hMcaspRxChan == NULL)) { MCASP_log("mcaspCreateChan for McASP1 Rx Failed\n"); BIOS_exit(0); } #if defined(MCASP_MASTER) /* If MCASP master, configure the clock of the slave device attached to McASP now. In the below case, it is the AIC codec */ #if defined(AIC_CODEC) status = aic31MdCreateChan( &hAicChannel, hAicDev, remName, mode, (Ptr)(&AIC31_config), (IOM_TiomCallback)&mcaspAppCallback, NULL); if ((NULL == hAicChannel) && (IOM_COMPLETED != status)) { MCASP_log("AIC Create Channel Failed\n"); } else { } #endif #endif } /* * ======== prime ======== */ MCASP_Packet rxFrame[NUM_BUFS]; MCASP_Packet txFrame[NUM_BUFS]; #include Hwi_Handle myHwi; static Void prime() { Error_Block eb; int32_t count = 0, status; IHeap_Handle iheap; iheap = HeapMem_Handle_to_xdc_runtime_IHeap(myHeap); Error_init(&eb); /* Allocate buffers for the SIO buffer exchanges */ for(count = 0; count < (NUM_BUFS ); count ++) { rxbuf[count] = Memory_calloc(iheap, BUFSIZE * RX_NUM_SERIALIZER, BUFALIGN, &eb); if(NULL == rxbuf[count]) { MCASP_log("\r\nMEM_calloc failed.\n"); } } /* Allocate work buffers for signal processing */ for(count = 0; count < NUM_BUFS; count++) { wkbuf[count] = Memory_calloc(iheap, (BUFSIZE * RX_NUM_SERIALIZER/(SYS_FS_RATIO*2)), BUFALIGN, &eb); if(NULL == wkbuf[count]) { IFPRINT(cmb_write("\r\nMEM_calloc failed for Wk\n")); IFPRINT(UART_printf("\r\nMEM_calloc failed for Wk\n")); } } /* Allocate buffers for the SIO buffer exchanges */ for(count = 0; count < (NUM_BUFS); count ++) { txbuf[count] = Memory_calloc(iheap, BUFSIZE * TX_NUM_SERIALIZER, BUFALIGN, &eb); if(NULL == txbuf[count]) { MCASP_log("\r\nMEM_calloc failed.\n"); } } /* Allocate output buffers for the MSS */ for(count = 0; count < NUM_BUFS; count++) { outbuf[count] = Memory_calloc(iheap, (BUFSIZE * TX_NUM_SERIALIZER/(SYS_FS_RATIO*2)), BUFALIGN, &eb); if(NULL == outbuf[count]) { IFPRINT(cmb_write("\r\nMEM_calloc failed for Out\n")); IFPRINT(UART_printf("\r\nMEM_calloc failed for Out\n")); } } for(count = 0; count < NUM_BUFS; count ++) { /* Issue the first & second empty buffers to the input stream */ memset((uint8_t *)rxbuf[count], 0xFF, BUFSIZE * RX_NUM_SERIALIZER); memset((uint8_t *)wkbuf[count], 0xBB, (BUFSIZE * RX_NUM_SERIALIZER/(SYS_FS_RATIO*2))); /* RX frame processing */ rxFrame[count].cmd = MCASP_READ; rxFrame[count].addr = (void*)(getGlobalAddr((uint32_t)rxbuf[count])); rxFrame[count].size = BUFSIZE * RX_NUM_SERIALIZER; rxFrame[count].arg = (uint32_t) hMcaspRxChan; rxFrame[count].status = 0; rxFrame[count].misc = 1; /* reserved - used in callback to indicate asynch packet */ /* Submit McASP packet for Rx */ status = mcaspSubmitChan(hMcaspRxChan, &rxFrame[count]); if((status != MCASP_PENDING)) MCASP_log ("Debug: Error McASP2 RX : Prime buffer #%d submission FAILED\n", count); } for(count = 0; count < (NUM_BUFS); count ++) { memset((uint8_t *)txbuf[count], 0xF0, BUFSIZE * TX_NUM_SERIALIZER); memset((uint8_t *)outbuf[count], 0xDD, (BUFSIZE * TX_NUM_SERIALIZER/(SYS_FS_RATIO*2))); /* TX frame processing */ txFrame[count].cmd = MCASP_WRITE; txFrame[count].addr = (void*)(getGlobalAddr((uint32_t)txbuf[count])); txFrame[count].size = BUFSIZE * TX_NUM_SERIALIZER; txFrame[count].arg = (uint32_t) hMcaspTxChan; txFrame[count].status = 0; txFrame[count].misc = 1; /* reserved - used in callback to indicate asynch packet */ /* Submit McASP packet for Tx */ status = mcaspSubmitChan(hMcaspTxChan, &txFrame[count]); if((status != MCASP_PENDING)) MCASP_log ("Debug: Error McASP2 TX : Prime buffer #%d submission FAILED\n", count); } } extern EDMA3_DRV_GblConfigParams sampleEdma3GblCfgParams[]; /* EnableEDMA event in the SampleCfg*/ static void enableEDMAHwEvent(uint32_t edmaNum, uint32_t eventNo) { sampleEdma3GblCfgParams[edmaNum].dmaChannelHwEvtMap[eventNo/32] |= (1 << (eventNo%32)); } /* * ======== echo ======== * This function copies from the input SIO to the output SIO. You could * easily replace the copy function with a signal processing algorithm. */ extern Int aic31MdBindDev(Ptr *, Int, Ptr); int gtxFrameIndexCount=0; int grxFrameIndexCount=0; int itemp; int result, pwr_status, fs_status, bck_status; int total_frames_sent=0; #define audDumpSec 10 #define sampNumSec 16000 #define audDumpSampleNum = audDumpSec*sampNumSec*TX_NUM_SERIALIZER int *audDumpBufPtr = (int *)0xc3800000; int audDumpBufIdx = 0; Void Audio_echo_Task() { volatile int32_t i32Count, status = 0; hMcaspDev = NULL; int count, serNum; int32_t i, j, k; int *tmpTxPtr, *tmpRxPtr; unsigned char *tempTxPtr, *tempRxPtr, *tempWkPtr; unsigned char *tempOutPtr, *tempMicPtr; tint nmics, nvmics, err, angle; volatile tulong t1, t2; /* for profiling */ tulong delta; void *inst_p; linSample *in_r; /* pointer to current microphone input buffer */ linSample *frame_p; /* pointer to signal frame */ linSample *outframe_p; /* Output frame pointer for VAU */ linSample *mics_in[SYS_MICS_MAX+1]; /* pointers to microphone inputs */ mssDebugStat_t mssDbg; #ifdef MEASURE_TIME profiling_init(); #endif /* 1. EDMA Initializations */ EDMA3_DRV_Result edmaResult = 0; enableEDMAHwEvent(EDMACC_NUM,MCASP_RX_DMA_CH); enableEDMAHwEvent(EDMACC_NUM,MCASP_TX_DMA_CH); hEdma = edma3init(EDMACC_NUM, &edmaResult); if (edmaResult != EDMA3_DRV_SOK) { /* Report EDMA Error */ MCASP_log("\nEDMA driver initialization unsuccessful\n"); } else { MCASP_log("\nEDMA driver initialization successful.\n"); } /* 2. SEM Initializations */ Semaphore_Params_init(¶ms); /* Create semaphores to wait for buffer reclaiming */ semR = Semaphore_create(0, ¶ms, &eb); semT = Semaphore_create(0, ¶ms, &eb); /* 3. McASP Initializations */ /* Initialize McASP Tx and Rx parameters */ mcaspParams = Mcasp_PARAMS; status = mcaspBindDev(&hMcaspDev, MCASP_NUM, &mcaspParams); if((status != MCASP_COMPLETED) || (hMcaspDev == NULL)) { MCASP_log("mcaspBindDev for McASP1 Failed\n"); abort(); } #if defined(AIC_CODEC) /* Bind AIC Codec */ aic31MdBindDev(&hAicDev, 0, (Ptr)&Aic31_PARAMS); #endif /* Call createStream function to create I/O streams */ createStreams(); // set up the CMB for audio input PCM186XADCInit(); MCASP_log("Initialization complete. priming about to begin \n"); /* Call prime function to do priming */ prime(); MCASP_log("priming complete.\n"); MCASP_log("\n******** Audio Loopback demo ********\n"); MCASP_log("Send audio signals in to the EVM's audio-in port and hear the same audio in the audio-out port\n"); /* Forever loop to continously receviec and transmit audio data */ for (i32Count = 0; i32Count >= 0; i32Count++) { if(gblErrFlagXmt || gblErrFlagRcv) break; Semaphore_pend(semR, BIOS_WAIT_FOREVER); Semaphore_pend(semT, BIOS_WAIT_FOREVER); #ifdef MEASURE_TIME profiling_end(); #endif /* Reclaim full buffer from the input stream */ gtxFrameIndexCount=txFrameIndex; grxFrameIndexCount=rxFrameIndex; Cache_inv(rxbuf[grxFrameIndexCount],BUFSIZE * RX_NUM_SERIALIZER,Cache_Type_ALL, TRUE); /******************************* Sample Processing Begins ***************************/ /* (BUFLEN* RX_NUM_SERIALIZER) 32-bit samples samples have been accumulated in rxbuf[grxFrameIndexCount] now. Application specific processing on these samples, before sending it back to McASP via txbuf[grxFrameIndexCount]. APPLICATION SPECIFIC PROCESSING could be done here. Below are the few audio demos and their application specific processing shown below. */ /* DEFAULT CASE: Copy the frame received and send it back to Tx buffer. This way the audio received by McASP from the remote device, is loopbacked and sent back to the device here. */ // dump RX buffer to TX buffer ///memcpy(txbuf[gtxFrameIndexCount],rxbuf[grxFrameIndexCount],BUFSIZE * RX_NUM_SERIALIZER); // Mcasp_BufferFormat_MULTISER_MULTISLOT_SEMI_INTERLEAVED_1 #if 0 // loopback one serilizer (0[mic1/5], 1[mic4/8], 2[mic2/6] or 3[mic3/7]) of RX to TX serNum = 0; // loopback mic1/5 tmpRxPtr = (int *)rxbuf[grxFrameIndexCount]; tmpRxPtr += serNum*2; tmpTxPtr = (int *)txbuf[gtxFrameIndexCount]; for (count=0; count