301 lines
8.4 KiB
C
301 lines
8.4 KiB
C
#include "hal.h"
|
|
#include "dma/dmad.h"
|
|
|
|
#include "sspd_ac97.h"
|
|
|
|
/* SSP FIFO properties */
|
|
#define SSPD_HW_TXFIFO_DEPTH 16 /* TX FIFO size, units of 32bit (todo: HW readback?) */
|
|
#define SSPD_HW_RXFIFO_DEPTH 16 /* RX FIFO size, units of 32bit (todo: HW readback?) */
|
|
|
|
/*****************************************************************************
|
|
* Data size for each each DMA request
|
|
*
|
|
* Adjust hint:
|
|
*
|
|
* AC97DMA_REQ_FRAMES sampling_rate effective data_size(2ch) data_size(6ch)
|
|
* ------------------------------------------------------------------------------
|
|
* 4096 48k (ac97-fix) 85.33 ms 32768 bytes 98304 bytes
|
|
* 8192 48k (ac97-fix) 170.66 ms 65536 bytes 196608 bytes
|
|
* 10240 48k (ac97-fix) 213.33 ms 81920 bytes 245760 bytes
|
|
* 12288 48k (ac97-fix) 256.00 ms 98304 bytes 294912 bytes
|
|
* 20480 48k (ac97-fix) 426.66 ms 163840 bytes 491520 bytes
|
|
****************************************************************************/
|
|
#define AC97DMA_REQ_FRAMES (20480) /* number of frames */
|
|
|
|
#define AC97_RESET_WAIT (0x600000) /* polling loop counter for waiting hw-reset */
|
|
|
|
enum SSPD_AC97_RESET {
|
|
|
|
SSPD_AC97_COLDRESET, /* All AC97 logic is initialized to its default state */
|
|
SSPD_AC97_WARMRESET, /* Contents of AC97 registers are left unaltered */
|
|
SSPD_AC97_REGRESET, /* Only Initialize the AC97 registers to their default states */
|
|
};
|
|
|
|
void sspd_ac97_sdata_out(uint32_t *txb, int cnt)
|
|
{
|
|
while (cnt > 0) {
|
|
|
|
uint32_t tfve;
|
|
|
|
/* Check room in TX FIFO */
|
|
tfve = MASK32(I2SAC97_SR, SSPC_SR_TFVE_MASK) >> SSPC_SR_TFVE_SHIFT;
|
|
|
|
/* Burst send to TX FIFO */
|
|
while (tfve++ < SSPD_HW_TXFIFO_DEPTH) {
|
|
|
|
/* Send one 32-bit word to TX FIFO */
|
|
OUT32(I2SAC97_DR, *txb++);
|
|
|
|
if (--cnt == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void sspd_ac97_cmd_out(int regidx, uint32_t data)
|
|
{
|
|
uint32_t txb[16];
|
|
|
|
/* Prepare AC97 write register address (slot1) and data (slot2) */
|
|
AC97_MAKE_WCMD(txb, regidx, data);
|
|
|
|
/* Clear SSP FIFO garbage */
|
|
SETR32(I2SAC97_CR2, SSPC_C2_RXFCLR_MASK | SSPC_C2_TXFCLR_MASK);
|
|
|
|
/* Set frame-tag slot-valid bits */
|
|
OUT32(I2SAC97_ACLINK, SSPC_AC97_WCMD_SLOTS_MASK | SSPC_AC97_MAKE_CODECID(0));
|
|
|
|
/* Feed data to TX FIFO -- AC97 CR-write contains 2 slots */
|
|
|
|
/*
|
|
* [??] According to AC97 2.1 spec, stuff bits with 0 has to be at their
|
|
* position during the slot's active time. SSP will smart enough to
|
|
* identify giving valid slots and auto stuffs 0s to empty slots in TX
|
|
* mode? And whot about the same question in RX mode?
|
|
*/
|
|
|
|
sspd_ac97_sdata_out(txb, SSPC_AC97_WCMD_SLOTS);
|
|
|
|
/* Enable SSP TX data out */
|
|
SETR32(I2SAC97_CR2, SSPC_C2_TXDOE_MASK | SSPC_C2_SSPEN_MASK);
|
|
|
|
while (MASK32(I2SAC97_SR, SSPC_SR_TFVE_MASK))
|
|
;
|
|
|
|
/* Disable SSP TX data out */
|
|
CLRR32(I2SAC97_CR2, SSPC_C2_TXDOE_MASK | SSPC_C2_SSPEN_MASK);
|
|
}
|
|
|
|
void sspd_ac97_reset(enum SSPD_AC97_RESET rest_type)
|
|
{
|
|
uint32_t core_intl;
|
|
|
|
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
|
|
|
|
/* Disable SSP interrupts */
|
|
CLRR32(I2SAC97_INTCR, SSPC_INTCR_RFORIEN_MASK | SSPC_INTCR_TFURIEN_MASK |
|
|
SSPC_INTCR_RFTHIEN_MASK | SSPC_INTCR_TFTHIEN_MASK |
|
|
SSPC_INTCR_RFDMAEN_MASK | SSPC_INTCR_TFDMAEN_MASK |
|
|
SSPC_INTCR_AC97FCEN_MASK);
|
|
|
|
/* Disable SSP data out */
|
|
CLRR32(I2SAC97_CR2, SSPC_C2_SSPEN_MASK | SSPC_C2_TXDOE_MASK);
|
|
|
|
/* Disable DMA request FIFO trigger */
|
|
CLRR32(I2SAC97_INTCR, SSPC_INTCR_TFDMAEN_MASK | SSPC_INTCR_RFDMAEN_MASK);
|
|
|
|
/* Clear FIFO garbage */
|
|
SETR32(I2SAC97_CR2, SSPC_C2_RXFCLR_MASK | SSPC_C2_TXFCLR_MASK);
|
|
|
|
/* Set SSP frame format as AC97 */
|
|
SETR32SHL(I2SAC97_CR0, SSPC_C0_FFMT_MASK, SSPC_C0_FFMT_SHIFT, SSPC_INTEL_ACLINK);
|
|
|
|
switch (rest_type) {
|
|
|
|
case SSPD_AC97_COLDRESET: /* All AC97 logic is initialized to its default state */
|
|
|
|
/* (reset time: SSPCLK * SCLK_DIV) */
|
|
DEBUG(1, 1, "SSPD_AC97_COLDRESET\n");
|
|
SETB32(I2SAC97_CR2, SSPC_C2_ACCRST_BIT);
|
|
|
|
while (GETB32(I2SAC97_CR2, SSPC_C2_ACCRST_BIT))
|
|
;
|
|
_nds_kwait(AC97_RESET_WAIT);
|
|
|
|
break;
|
|
case SSPD_AC97_WARMRESET: /* Contents of AC97 registers are left unaltered */
|
|
|
|
/* (reset time: SSPCLK * SCLK_DIV, or wait ACWRST cleared) */
|
|
DEBUG(1, 1, "SSPD_AC97_WARMRESET\n");
|
|
SETB32(I2SAC97_CR2, SSPC_C2_ACWRST_BIT);
|
|
|
|
while (GETB32(I2SAC97_CR2, SSPC_C2_ACWRST_BIT))
|
|
;
|
|
|
|
break;
|
|
case SSPD_AC97_REGRESET: /* Only Initialize the AC97 registers to their default states */
|
|
|
|
DEBUG(1, 1, "SSPD_AC97_REGRESET\n");
|
|
|
|
/* Write AC97 reset register to do codec register reset */
|
|
sspd_ac97_cmd_out(AC97_CRIDX_RESET, 0);
|
|
_nds_kwait(AC97_RESET_WAIT);
|
|
|
|
break;
|
|
default:
|
|
DEBUG(1, 1, "Invalid reset method!\n");
|
|
}
|
|
|
|
hal_global_int_ctl(core_intl);
|
|
}
|
|
|
|
void sspd_ac97_init(void)
|
|
{
|
|
uint32_t core_intl;
|
|
|
|
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
|
|
|
|
/*
|
|
* Change AC97 codec & SSP clock source
|
|
*
|
|
* PMU_AC97PINSEL: MFPSR[3]
|
|
* 0: X_I2Ssclkout/I2SCLK
|
|
* 1: X_ac97_resetn/50MHz in AG101
|
|
* PMU_AC97CLKSEL: MFPSR[4]
|
|
* 0: AC97CLK (Set AC97 XTL_IN source is from internal PLL. BIT_CLK is XTL_IN / 2)
|
|
* 1: GPIO22
|
|
* PMU_SSPCLKSEL: MFPSR[6]
|
|
* 0: SSPCLK
|
|
* 1: GPIO25
|
|
* PMU_AC97CLKOUTSEL: MFPSR[13]
|
|
* 0: GPIO
|
|
* 1: AC97CLK out
|
|
*/
|
|
|
|
// SETR32(PMU_MFPSR, PMU_AC97PINSEL_MASK | PMU_AC97CLKSEL_MASK | PMU_SSPCLKSEL_MASK | PMU_AC97CLKOUTSEL_MASK);
|
|
// SETR32(PMU_MFPSR, PMU_AC97PINSEL_MASK | PMU_AC97CLKOUTSEL_MASK);
|
|
// SETB32(PMU_MFPSR, PMU_AC97CLKSEL_BIT);
|
|
|
|
#if (MB_AC97_EXT_CLK)
|
|
DEBUG(1, 1, "AC97CLK: GPIO22\n");
|
|
SETR32(PMU_MFPSR, PMU_AC97PINSEL_MASK | PMU_AC97CLKSEL_MASK | PMU_AC97CLKOUTSEL_MASK);
|
|
#else /* MB_AC97_EXT_CLK */
|
|
DEBUG(1, 1, "AC97CLK: PLL\n");
|
|
SETR32(PMU_MFPSR, PMU_AC97PINSEL_MASK | PMU_AC97CLKOUTSEL_MASK);
|
|
CLRB32(PMU_MFPSR, PMU_AC97CLKSEL_BIT);
|
|
#endif /* MB_AC97_EXT_CLK */
|
|
|
|
sspd_ac97_reset(SSPD_AC97_COLDRESET);
|
|
|
|
/* Setup DMA FIFO trigger threshold */
|
|
SETR32SHL(I2SAC97_INTCR, SSPC_INTCR_TFTHOD_MASK, SSPC_INTCR_TFTHOD_SHIFT, 0);
|
|
SETR32SHL(I2SAC97_INTCR, SSPC_INTCR_RFTHOD_MASK, SSPC_INTCR_RFTHOD_SHIFT, 0);
|
|
|
|
/* SSP AC97 codec initialization */
|
|
|
|
/*
|
|
* Default master volume?
|
|
* Default mixer-in gain?
|
|
* Default record input selection?
|
|
*/
|
|
//sspd_ac97_cmd_out(AC97_CRIDX_MASTER_VOLUME, 0);
|
|
|
|
hal_global_int_ctl(core_intl);
|
|
}
|
|
|
|
void sspd_ac97_terminate(void)
|
|
{
|
|
uint32_t core_intl;
|
|
|
|
core_intl = hal_global_int_ctl(HAL_DISABLE_INTERRUPTS);
|
|
|
|
/* Disable SSP interrupts */
|
|
CLRR32(I2SAC97_INTCR, SSPC_INTCR_RFORIEN_MASK | SSPC_INTCR_TFURIEN_MASK |
|
|
SSPC_INTCR_RFTHIEN_MASK | SSPC_INTCR_TFTHIEN_MASK |
|
|
SSPC_INTCR_RFDMAEN_MASK | SSPC_INTCR_TFDMAEN_MASK |
|
|
SSPC_INTCR_AC97FCEN_MASK);
|
|
|
|
/* Disable SSP data out */
|
|
CLRR32(I2SAC97_CR2, SSPC_C2_SSPEN_MASK | SSPC_C2_TXDOE_MASK);
|
|
|
|
/* Cold reset AC97 codec */
|
|
SETB32(I2SAC97_CR2, SSPC_C2_ACCRST_BIT);
|
|
while (GETB32(I2SAC97_CR2, SSPC_C2_ACCRST_BIT))
|
|
;
|
|
|
|
/* Clear FIFO garbage */
|
|
SETR32(I2SAC97_CR2, SSPC_C2_RXFCLR_MASK | SSPC_C2_TXFCLR_MASK);
|
|
|
|
hal_global_int_ctl(core_intl);
|
|
}
|
|
|
|
void ac97_init(void)
|
|
{
|
|
sspd_ac97_init();
|
|
|
|
sspd_ac97_cmd_out(AC97_CRIDX_RESET, 0);
|
|
_nds_kwait(AC97_RESET_WAIT);
|
|
|
|
sspd_ac97_cmd_out(AC97_CRIDX_PCMOUT_GAIN,
|
|
AC97_MIXER_GAIN(AC97_MIXER_MAX, AC97_MIXER_MAX));
|
|
|
|
sspd_ac97_cmd_out(AC97_CRIDX_MASTER_VOLUME,
|
|
AC97_STEREO_VOLUME(AC97_VOLUME_MAX - 0x30, AC97_VOLUME_MAX - 0x30));
|
|
|
|
SETR32SHL(I2SAC97_INTCR, SSPC_INTCR_TFTHOD_MASK, SSPC_INTCR_TFTHOD_SHIFT, 4);
|
|
SETR32SHL(I2SAC97_INTCR, SSPC_INTCR_RFTHOD_MASK, SSPC_INTCR_RFTHOD_SHIFT, 4);
|
|
OUT32(I2SAC97_ACLINK, SSPC_AC97_PCM_SLOTS_MASK | SSPC_AC97_MAKE_CODECID(0));
|
|
SETR32(I2SAC97_INTCR, SSPC_INTCR_TFDMAEN_MASK);
|
|
SETR32(I2SAC97_CR2, SSPC_C2_TXDOE_MASK | SSPC_C2_SSPEN_MASK);
|
|
SETR32(I2SAC97_CR2, SSPC_C2_RXFCLR_MASK | SSPC_C2_TXFCLR_MASK);
|
|
}
|
|
|
|
volatile int g_buffered_frames;
|
|
|
|
/* No use and marked by KCLin */
|
|
/*
|
|
static void psp(void *data)
|
|
{
|
|
g_buffered_frames -= *(int*)data;
|
|
free(data);
|
|
|
|
}
|
|
*/
|
|
|
|
static void rcp(void *data)
|
|
{
|
|
g_buffered_frames -= *(int*)data;
|
|
free(data);
|
|
}
|
|
extern int ring_idx;
|
|
void ac97_play(int frames, uint32_t *pcm_data, void *ac97_data)
|
|
{
|
|
DMAD_CHANNEL_REQUEST_DESC *ch_req = ac97_data;
|
|
|
|
while (frames > 0){
|
|
|
|
int f;
|
|
int *data;
|
|
DMAD_DRB *drb;
|
|
|
|
f = (frames < AC97DMA_REQ_FRAMES) ? frames : AC97DMA_REQ_FRAMES;
|
|
data = malloc(sizeof(int));
|
|
KASSERT(data);
|
|
*data = f;
|
|
|
|
_dmad_alloc_drb(ch_req, &drb);
|
|
|
|
drb->src_addr = pcm_data;
|
|
drb->dst_addr = (void *)I2SAC97_DR;
|
|
drb->req_size = (f << 1); /* units of data width (32bit for AC97) */
|
|
drb->rcp = rcp;
|
|
drb->data = data;
|
|
|
|
pcm_data += (f << 1);
|
|
g_buffered_frames += f;
|
|
// DEBUG(1, 1, "FRAME: %d,%d\n", g_buffered_frames,ring_idx);
|
|
_dmad_submit_request(ch_req, drb);
|
|
frames -= f;
|
|
}
|
|
}
|