2025-12-17 15:55:25 +08:00

680 lines
16 KiB
C

/***************************************************************************
* 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);
}