/*************************************************************************** * Name:ssp.c * * Description: SSP C library routine * * Author: * ****************************************************************************/ #include "common_include.h" #include "Driver_Common.h" #include "kdrv_i2s.h" //fLib_ssp *SSPDevice[] ={0, (fLib_ssp *) CPE_SSP1_BASE, // (fLib_ssp *) CPE_I2S_BASE,(fLib_ssp *)CPE_AC97_BASE,(fLib_ssp *) CPE_SSP2_BASE}; /* PLL=24.576M FPclk(For 6631)=24.576/2=12.288M FPclk(For AD73311)=24.576/8=3.072M FSclk=8K or 32K fBclk=FSclk*48 or FSclk*64 stuff_bit=48/2-16=8 or 64/2-16=16 FBclk= Fsclk*48(64)=Fpclk/2*(SCLK_DIV+1) ex: Fsclk=8k Fpclk=3072K SCLK_DIV=(3072K/8K*48(64)/2)-1=3 */ /* PLL=24.576M FMclk(For AC97 Winbond 83972d)=24.576M FBclk=12.288M FSclk=48K(AC97) FBclk= Fsclk*256=FMclk/2*(SCLK_DIV+1) */ int TxFIFOLen = 0; int RxFIFOLen = 0; void fLib_I2S_Init(u32 base_address, u32 codec, SSP_MODE_T mode) { u32 data; unsigned int master_slave; if(mode == SSP_AS_MASTER) master_slave = SSP_OPM_MSST; else master_slave = SSP_OPM_SLST; if( codec == W6631TS_I2S ) { // FDIST = 1, 24 bits, padding 7 bits, word_length = 16 bits // SSP main clock input clock = source clock / 2 // outw(SSPCK_BASE,0x00); data = I2S_Format | 0x100; outw(base_address + SSP_CONTROL0,data); fLib_SetSSPOPMode(base_address, master_slave); fLib_SetSSPDataLen(base_address,15); fLib_SetSSPpaddingDataLen(base_address,6); fLib_SetSSPClkdiv(base_address,3); } else if ( codec == UDA1345TS_I2S ) { //UDA1345TS's distance between fram/sync and first data ==> FSDIST = 1 // 16 bits, MSB first, SSP_DATAJUSTIFY set to 1 ==> pad data in the front of serial data // 16.9344MHz clock => 22.05K sample rate //outw(SSPCK_BASE,0x00); //dvided 2 data = I2S_Format | 0x100 | SSP_DATAJUSTIFY;// frame sync must shift 1 bits when I2S mode outw(base_address + SSP_CONTROL0,data); fLib_SetSSPOPMode(base_address, master_slave); fLib_SetSSPDataLen(base_address,0xF); fLib_SetSSPClkdiv(base_address,0x3); } else if ( codec == NORMAL_I2S ) { // 16 bits, FDIST = 1, padding data in the back of serial data // 16.9344MHz clock => 22.05K sample rate // outw(SSPCK_BASE,0x00); //dvided 2 data = I2S_Format | 0x100; outw(base_address + SSP_CONTROL0,data); fLib_SetSSPOPMode(base_address, master_slave); fLib_SetSSPDataLen(base_address,0xF); fLib_SetSSPClkdiv(base_address,0x5); } else if ( codec == FSA0AC108_I2S ) { // 16 data bits, 16 padding data bits // FDIST = 1, padding data in the back of serial data // 12.288MHz clock data = I2S_Format | 0x100 | SSP_FSPO_LOW; outw(base_address + SSP_CONTROL0,data); fLib_SetSSPOPMode(base_address, master_slave); /* ssp as master */ fLib_SetSSPDataLen(base_address,0xF); fLib_SetSSPpaddingDataLen(base_address, 0x10); fLib_SetSSPClkdiv(base_address,0xB); } else if ( (codec == WM8510_I2S)||(codec == WM8731S_I2S) ) { /* If SSP is set as a master, enable SCU+0x330 to allow MCLK to be given by PLL */ #if 0 if(mode == SSP_AS_MASTER) vLib_LeWrite32(SCU_FTSCU100_PA_BASE + 0x330, 1); else vLib_LeWrite32(SCU_FTSCU100_PA_BASE + 0x330, 0); #endif // 16 data bits, 16 padding data bits // FDIST = 1, padding data in the back of serial data // 12.288MHz clock data = I2S_Format | 0x100 | SSP_FSPO_LOW; outw(base_address + SSP_CONTROL0,data); fLib_SetSSPOPMode(base_address, master_slave); fLib_SetSSPDataLen(base_address,0xF); if(mode == SSP_AS_MASTER) { /* As a master, SSP's MCLK is given by PLL (=12.288MHz) with SCU_FTSCU100_PA_BASE + 0x330 = 1. Then, SCLK/BCLK (=3.072MHz) is generated by setting divider (SCLKDIV) to 1 and outputs it to codec (WM8731) as a slave, so 16-bit padding is needed for 48KHz-16bit stereo data. 48K (sample rate) * 32 (bit resolution + padding per channel) * 2 (left and right channels) = 3.072M */ fLib_SetSSPClkdiv(base_address,0x01); fLib_SetSSPpaddingDataLen(base_address, 0x10); } else { /* As a slave, SSP whose SCLK/BCLK (=3.072MHz) are given by external codec (WM8731), so 16-bit padding is needed for 48KHz-16bit stereo data. 48K (sample rate) * 32 (bit resolution + padding per channel) * 2 (left and right channels) = 3.072M */ fLib_SetSSPpaddingDataLen(base_address, 0x10); } } else if( codec == WM9081_I2S) { if(mode == SSP_AS_SLAVE) { /* SCLK (BCLK) and FS (LRCLK or sample rate) of SSP010, as SLAVE, are given by WM9081 codec as MASTER where FS = 48KHz SCLK = FS * 16 (bit resolution per channel) * 2 (left and right channels) = 1.536MHz SSPCLK is input by internal PLL at 12.288MHz, which suits the minimum ratio of SSPCLK/SCLK (12.288MHz/1.536MHz >= 6) */ /* Set SCU+0x330 to 1 to enable PLL to generate 12.288 MHz for SSPCLK of SSP010 */ vLib_LeWrite32(SCU_FTSCU100_PA_BASE + 0x330, 1); /* I2S format, FDIST = 1, frame sync active low */ data = I2S_Format | 0x100 | SSP_FSPO_LOW; outw(base_address + SSP_CONTROL0,data); /* Slave stereo mode */ fLib_SetSSPOPMode(base_address, master_slave); // 16 data bits, no padding fLib_SetSSPDataLen(base_address,0xF); fLib_SetSSPpaddingDataLen(base_address, 0x0); } } else if ( codec == PCM3793_I2S ) { data = I2S_Format | 0x100 | SSP_FSPO_LOW; outw(base_address + SSP_CONTROL0,data); fLib_SetSSPOPMode(base_address, master_slave); fLib_SetSSPDataLen(base_address,0xF); fLib_SetSSPClkdiv(base_address,0x3); } } void fLib_SetSSPFrame(u32 base_address,u32 frame) { u32 data; data = inw(base_address + SSP_CONTROL0); data &= 0xFFF; data |= frame; outw(base_address + SSP_CONTROL0, data); } void fLib_SetSSPFramePolar(u32 base_address,u32 polar) { u32 data; data = inw(base_address + SSP_CONTROL0); data &= ~0x20; data |= polar; outw(base_address + SSP_CONTROL0, data); } void fLib_SetSSPOPMode(u32 base_address,u32 mode) { u32 data; data = inw(base_address + SSP_CONTROL0); data &= ~0xC; data |= mode; outw(base_address + SSP_CONTROL0, data); } void fLib_SetSSPDataLen(u32 base_address,u32 len) { u32 data; data = inw(base_address + SSP_CONTROL1); data &= ~ SSP_SDL; data |= len << 16; outw(base_address + SSP_CONTROL1, data); } void fLib_SetSSPpaddingDataLen(u32 base_address,u32 len) { u32 data; data = inw(base_address + SSP_CONTROL1); data &= ~ SSP_PDL; data |= len << 24; outw(base_address + SSP_CONTROL1, data); } void fLib_SetSSPClkdiv(u32 base_address,u32 divider) { u32 data; data = inw(base_address + SSP_CONTROL1); data &= ~ SSP_CLKDIV; data |= divider; outw(base_address + SSP_CONTROL1, data); } void fLib_SSPClearTxFIFO(u32 base_address) { u32 data; data = inw(base_address + SSP_CONTROL2); data |= SSP_TXFCLR; outw(base_address + SSP_CONTROL2, data); } void fLib_SSPClearRxFIFO(u32 base_address) { u32 data; data = inw(base_address + SSP_CONTROL2); data |= SSP_RXFCLR; outw(base_address + SSP_CONTROL2, data); } void fLib_WriteSSP(u32 base_address,u32 data) { outw(base_address + SSP_DATA, data); } u32 fLib_ReadSSPStatus(u32 base_address) { u32 data; data = inw(base_address + SSP_STATUS); return data; } u32 fLib_ReadSSP(u32 base_address) { u32 data; data = inw(base_address + SSP_DATA); return data; } void fLib_SetSSPSCLKPO(u32 base_address,u32 spolarity) { u32 data; data = inw(base_address + SSP_CONTROL0); data &= ~0x2; data |= spolarity; outw(base_address + SSP_CONTROL0, data); } void fLib_SetSSPSCLKPH(u32 base_address,u32 sphase) { u32 data; data = inw(base_address + SSP_CONTROL0); data &= ~0x1; data |= sphase; outw(base_address + SSP_CONTROL0, data); } u32 fLib_ReadSSPIntStatus(u32 base_address) { u32 data; data = inw(base_address + SSP_INT_STATUS); return data; } void fLib_SetSSP_TXFIFO(u32 base_address,u32 threshold,u32 underrun) { u32 data; data = inw(base_address + SSP_INT_CONTROL); if (threshold) data |= SSP_TFIEN; else data &= ~SSP_TFIEN; if (underrun) data |= SSP_TFURIEN; else data &= ~SSP_TFURIEN; outw(base_address + SSP_INT_CONTROL, data); } void fLib_SetSSP_RXFIFO(u32 base_address,u32 threshold,u32 underrun) { u32 data; data = inw(base_address + SSP_INT_CONTROL); if (threshold) data |= SSP_RFIEN; else data &= ~SSP_RFIEN; if (underrun) data |= SSP_RFORIEN; else data &= ~SSP_RFORIEN; outw(base_address + SSP_INT_CONTROL, data); } void fLib_SetSSP_DMA(u32 base_address,u32 trans,u32 rec) { u32 data; data = inw(base_address + SSP_INT_CONTROL); if (trans) data |= SSP_TXDMAEN; else data &= ~SSP_TXDMAEN; if (rec) data |= SSP_RXDMAEN; else data &= ~SSP_RXDMAEN; outw(base_address + SSP_INT_CONTROL, data); } void fLib_SetSSP_FIFO_Threshold(u32 base_address,u32 trans_len,u32 rec_len) { u32 ctrl; ctrl = inw(base_address + SSP_INT_CONTROL); ctrl &= ~0x0001FF80; //bessel:todo:length start with 7th bit not 8bit //ctrl |= ((trans_len << 12) + (rec_len << 8)) & 0x0000FF00; ctrl |= ((trans_len << 12) + (rec_len << 7)); outw(base_address + SSP_INT_CONTROL, ctrl); } void fLib_SetSSP_WarmReset(u32 base_address) { u32 ctrl; outw(base_address + SSP_CONTROL2, SSP_ACWRST | SSP_TXDOE); do { ctrl = inw(base_address + SSP_CONTROL2); }while((ctrl & SSP_ACWRST) != 0); } void fLib_SetSSP_ColdReset(u32 base_address) { u32 ctrl; outw(base_address + SSP_CONTROL2, SSP_ACCRST | SSP_TXDOE); // do { ctrl = inw(base_address + SSP_CONTROL2); }while((ctrl & SSP_ACCRST) != 0); } void fLib_SetSSP_Enable(u32 base_address,int enable) { u32 ctrl; ctrl = inw(base_address + SSP_CONTROL2); if(enable) ctrl |= (SSP_SSPEN | SSP_TXDOE | SSP_TXEN | SSP_RXEN);//TXDOE is a must; otherwise, no sound at all to output else ctrl &= ~(SSP_SSPEN + SSP_TXDOE | SSP_TXEN | SSP_RXEN); outw(base_address + SSP_CONTROL2, ctrl); } void fLib_SetSSP_IntMask(u32 base_address,int Mask) { u32 ctrl; ctrl = inw(base_address + SSP_INT_CONTROL); ctrl &= ~0x3F; ctrl |= Mask; outw(base_address + SSP_INT_CONTROL, ctrl); } u32 fLib_SSP_GetTxFIFOLen(u32 base_address) { u32 len; len = (inw(base_address + SSP_INFO)&SSP_TXFIFO_DEPTH); TxFIFOLen = (len >> 16) + 1; return TxFIFOLen; } u32 fLib_SSP_GetRxFIFOLen(u32 base_address) { u32 len; len = (inw(base_address + SSP_INFO)&SSP_RXFIFO_DEPTH); RxFIFOLen = (len >> 8) + 1; return RxFIFOLen; } u32 fLib_SSP_GetRxFIFOValidEntries(u32 base_addr) { return ((fLib_ReadSSPStatus(base_addr) & SSP_RFVE) >> 4); } void fLib_AC97_SetSlotValidReg(u32 base_address,u32 SlotValid) { outw(base_address + SSP_ACLINK_SLOT_VALID, SlotValid); } void fLib_InitAC97(u32 base_address) { // outw(SSPCK_BASE, 0xF00); fLib_SetSSPFrame(base_address,AC97_Format); fLib_SSP_GetTxFIFOLen(base_address); fLib_SSP_GetRxFIFOLen(base_address); fLib_SetSSP_ColdReset(base_address); fLib_SetSSP_Enable(base_address,false); fLib_SetSSP_IntMask(base_address,0); fLib_SetSSP_FIFO_Threshold(base_address,4,4); } void fLib_AC97_WriteData(u32 base_address,u32 *data, u32 Len) { u32 tx_cnt; while(Len != 0) { tx_cnt = (inw(base_address + SSP_STATUS) & SSP_TFVE) >> 12; for(; tx_cnt < TxFIFOLen; tx_cnt++, data++, Len--) { if(Len == 0) return ; outw(base_address + SSP_DATA, *data); } } return; } void fLib_AC97_ReadData(u32 base_address,u32 *data, u32 Len) { u32 rx_cnt; while(Len != 0) { rx_cnt = (inw(base_address + SSP_STATUS) & SSP_RFVE) >> 4; for(; rx_cnt > 0; rx_cnt--, data++, Len--) { if(Len == 0) return; *data = inw(base_address + SSP_DATA); } } } u32 fLib_ReadSSP32Bit(u32 base_address) { u32 data; data = inw(base_address + SSP_DATA); return data; } u32 fLib_AC97_ReadOneWordData(u32 base_address) { while(!(inw(base_address + SSP_STATUS) & SSP_RFVE)) ; return inw(base_address + SSP_DATA); } int fLib_AC97_ReadRegister(u32 base_address,u32 Reg_Index, u16 *data) { u32 TxBuf[3], RxBuf[3]; TxBuf[0] = 0xE000; TxBuf[1] = 0x80000+(Reg_Index<<12); TxBuf[2] = 0; fLib_SSPClearTxFIFO(base_address); fLib_SSPClearRxFIFO(base_address); fLib_AC97_SetSlotValidReg(base_address,0x7); fLib_AC97_WriteData(base_address,TxBuf, 3); fLib_SetSSP_Enable(base_address,true); while(1) { fLib_AC97_ReadData(base_address,RxBuf, 1); //check reg index is match if((RxBuf[0] & 0xE000) == 0xE000) { fLib_AC97_ReadData(base_address,RxBuf, 1); if(((RxBuf[0] >> 12) & 0x7f) == Reg_Index) { fLib_AC97_ReadData(base_address,RxBuf, 1); *data = (RxBuf[0] >> 4) & 0x0000ffff; } fLib_SetSSP_Enable(base_address,false); break; } } return true; } int fLib_ReturnTxFIFO_Count(u32 base_address) { int count = 0; count = (inw(base_address+SSP_STATUS)&SSP_TFVE)>>12; return count; } int fLib_ReturnRxFIFO_Count(u32 base_address) { int count = 0; count = (inw(base_address+SSP_STATUS)&SSP_RFVE)>>4; return count; } int fLib_AC97_ReadRegisterEx(u32 base_address,u32 Reg_Index, u16 *data) { u32 TxBuf[2], RxBuf[2]; *data = 0xFFFF; TxBuf[0] = 0x80000+(Reg_Index<<12); // Command address slot (slot1) TxBuf[1] = 0; // Command Data port slot (slot2) fLib_SSPClearTxFIFO(base_address); fLib_SSPClearRxFIFO(base_address); fLib_AC97_SetSlotValidReg(base_address,0xE000); // slot0, slot1, slot2 //########################################################################## // when enable SSP controler, data will send immediately at the first frame, // but receive data will be at next frame // to make sure second frame will not send wrong data, // we must fill 2 set of command to prevent second data to be wrong //########################################################################## fLib_AC97_WriteData(base_address,TxBuf, 2); fLib_AC97_WriteData(base_address,TxBuf, 2); fLib_SetSSP_Enable(base_address,true); while(fLib_ReturnTxFIFO_Count(base_address)>0) ; fLib_SetSSP_Enable(base_address,false); // data will be in the second frame, so pump 2 fifo data first fLib_AC97_ReadData(base_address,RxBuf, 2); // read first frame (no use frame) // real valid data fLib_AC97_ReadData(base_address,RxBuf, 1); // read second frame - slot1(ECHO of Command address) if(((RxBuf[0] >> 12) & 0x7f) == Reg_Index) { fLib_AC97_ReadData(base_address,RxBuf, 1); // read second frame - slot2 (status data port) *data = (RxBuf[0] >> 4) & 0x0000ffff; } return true; } void fLib_AC97_WriteRegister(u32 base_address,u32 Reg_Index, u32 data) { u32 TxBuf[3]; TxBuf[0] = 0xE000; TxBuf[1] = 0x00000+(Reg_Index<<12); TxBuf[2] = data << 4; fLib_SSPClearTxFIFO(base_address); fLib_SSPClearRxFIFO(base_address); fLib_AC97_SetSlotValidReg(base_address,0x7); fLib_AC97_WriteData(base_address,TxBuf, 3); fLib_SetSSP_Enable(base_address,true); while(((inw(base_address+SSP_STATUS)&SSP_TFVE)>>12)>0) ; fLib_SetSSP_Enable(base_address,false); } void fLib_AC97_WriteRegisterEx(u32 base_address,u32 Reg_Index, u32 data) { u32 TxBuf[2]; TxBuf[0] = 0x00000+(Reg_Index<<12); TxBuf[1] = data << 4; fLib_SSPClearTxFIFO(base_address); fLib_SSPClearRxFIFO(base_address); fLib_AC97_SetSlotValidReg(base_address,0xE000); // slot0, slot1, slot2 fLib_AC97_WriteData(base_address,TxBuf, 2); fLib_SetSSP_Enable(base_address,true); while(fLib_ReturnTxFIFO_Count(base_address)>0) ; fLib_SetSSP_Enable(base_address,false); } u32 fLib_SSP_busy(u32 base_addr) { return (((fLib_ReadSSPStatus(base_addr) ) >> 2) & 0x1); } void fLib_SetSSP_SDL_Write(u32 base_address,u32 len,u32 w_data) { while (fLib_SSP_busy(base_address)) ; fLib_SetSSPDataLen(base_address,len); fLib_WriteSSP(base_address,w_data); }