diff options
-rw-r--r-- | firmware/drivers/audio/wm8978.c | 95 | ||||
-rw-r--r-- | firmware/export/wm8978.h | 11 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c | 93 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c | 96 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c | 83 |
5 files changed, 190 insertions, 188 deletions
diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c index 6836d8c5e8..5bba19b996 100644 --- a/firmware/drivers/audio/wm8978.c +++ b/firmware/drivers/audio/wm8978.c @@ -505,88 +505,7 @@ void audiohw_close(void) void audiohw_set_frequency(int fsel) { - /* For 16.9344MHz MCLK, codec as master. */ - static const struct - { - uint32_t plln : 8; - uint32_t pllk1 : 6; - uint32_t pllk2 : 9; - uint32_t pllk3 : 9; - unsigned char mclkdiv; - unsigned char filter; - } srctrl_table[HW_NUM_FREQ] = - { - [HW_FREQ_8] = /* PLL = 65.536MHz */ - { - .plln = 7 | WMC_PLL_PRESCALE, - .pllk1 = 0x2f, /* 12414886 */ - .pllk2 = 0x0b7, - .pllk3 = 0x1a6, - .mclkdiv = WMC_MCLKDIV_8, /* 2.0480 MHz */ - .filter = WMC_SR_8KHZ, - }, - [HW_FREQ_11] = /* PLL = off */ - { - .mclkdiv = WMC_MCLKDIV_6, /* 2.8224 MHz */ - .filter = WMC_SR_12KHZ, - }, - [HW_FREQ_12] = /* PLL = 73.728 MHz */ - { - .plln = 8 | WMC_PLL_PRESCALE, - .pllk1 = 0x2d, /* 11869595 */ - .pllk2 = 0x08e, - .pllk3 = 0x19b, - .mclkdiv = WMC_MCLKDIV_6, /* 3.0720 MHz */ - .filter = WMC_SR_12KHZ, - }, - [HW_FREQ_16] = /* PLL = 65.536MHz */ - { - .plln = 7 | WMC_PLL_PRESCALE, - .pllk1 = 0x2f, /* 12414886 */ - .pllk2 = 0x0b7, - .pllk3 = 0x1a6, - .mclkdiv = WMC_MCLKDIV_4, /* 4.0960 MHz */ - .filter = WMC_SR_16KHZ, - }, - [HW_FREQ_22] = /* PLL = off */ - { - .mclkdiv = WMC_MCLKDIV_3, /* 5.6448 MHz */ - .filter = WMC_SR_24KHZ, - }, - [HW_FREQ_24] = /* PLL = 73.728 MHz */ - { - .plln = 8 | WMC_PLL_PRESCALE, - .pllk1 = 0x2d, /* 11869595 */ - .pllk2 = 0x08e, - .pllk3 = 0x19b, - .mclkdiv = WMC_MCLKDIV_3, /* 6.1440 MHz */ - .filter = WMC_SR_24KHZ, - }, - [HW_FREQ_32] = /* PLL = 65.536MHz */ - { - .plln = 7 | WMC_PLL_PRESCALE, - .pllk1 = 0x2f, /* 12414886 */ - .pllk2 = 0x0b7, - .pllk3 = 0x1a6, - .mclkdiv = WMC_MCLKDIV_2, /* 8.1920 MHz */ - .filter = WMC_SR_32KHZ, - }, - [HW_FREQ_44] = /* PLL = off */ - { - .mclkdiv = WMC_MCLKDIV_1_5, /* 11.2896 MHz */ - .filter = WMC_SR_48KHZ, - }, - [HW_FREQ_48] = /* PLL = 73.728 MHz */ - { - .plln = 8 | WMC_PLL_PRESCALE, - .pllk1 = 0x2d, /* 11869595 */ - .pllk2 = 0x08e, - .pllk3 = 0x19b, - .mclkdiv = WMC_MCLKDIV_1_5, /* 12.2880 MHz */ - .filter = WMC_SR_48KHZ, - }, - }; - + extern const struct wmc_srctrl_entry wmc_srctrl_table[HW_NUM_FREQ]; unsigned int plln; unsigned int mclkdiv; @@ -594,10 +513,10 @@ void audiohw_set_frequency(int fsel) fsel = HW_FREQ_DEFAULT; /* Setup filters. */ - wmc_write(WMC_ADDITIONAL_CTRL, srctrl_table[fsel].filter); + wmc_write(WMC_ADDITIONAL_CTRL, wmc_srctrl_table[fsel].filter); - plln = srctrl_table[fsel].plln; - mclkdiv = srctrl_table[fsel].mclkdiv; + plln = wmc_srctrl_table[fsel].plln; + mclkdiv = wmc_srctrl_table[fsel].mclkdiv; if (plln != 0) { @@ -605,9 +524,9 @@ void audiohw_set_frequency(int fsel) /* Program PLL. */ wmc_write(WMC_PLL_N, plln); - wmc_write(WMC_PLL_K1, srctrl_table[fsel].pllk1); - wmc_write(WMC_PLL_K2, srctrl_table[fsel].pllk2); - wmc_write(WMC_PLL_K3, srctrl_table[fsel].pllk3); + wmc_write(WMC_PLL_K1, wmc_srctrl_table[fsel].pllk1); + wmc_write(WMC_PLL_K2, wmc_srctrl_table[fsel].pllk2); + wmc_write(WMC_PLL_K3, wmc_srctrl_table[fsel].pllk3); /* Turn on PLL. */ wmc_set(WMC_POWER_MANAGEMENT1, WMC_PLLEN); diff --git a/firmware/export/wm8978.h b/firmware/export/wm8978.h index 4081d05a89..f591c1b9fb 100644 --- a/firmware/export/wm8978.h +++ b/firmware/export/wm8978.h @@ -499,4 +499,15 @@ void wmc_clear(unsigned int reg, unsigned int bits); #define WMC_RMIX2OUT4 (1 << 1) #define WMC_RDAC2OUT4 (1 << 0) +/* For implementing samplerate conrol */ +struct wmc_srctrl_entry +{ + uint32_t plln : 8; + uint32_t pllk1 : 6; + uint32_t pllk2 : 9; + uint32_t pllk3 : 9; + unsigned char mclkdiv; + unsigned char filter; +}; + #endif /* _WM8978_H */ diff --git a/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c index 6291a9cf18..723999c15e 100644 --- a/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c @@ -20,6 +20,8 @@ ****************************************************************************/ #include "config.h" #include "system.h" +#include "ccm-imx31.h" +#include "sdma-imx31.h" #include "i2s.h" void i2s_reset(void) @@ -42,4 +44,95 @@ void i2s_reset(void) ((64-1) << CCM_PDR1_SSI2_PODF_POS), CCM_PDR1_SSI1_PODF | CCM_PDR1_SSI2_PODF | CCM_PDR1_SSI1_PRE_PODF | CCM_PDR1_SSI2_PRE_PODF); + + ccm_module_clock_gating(CG_SSI1, CGM_ON_RUN_WAIT); + ccm_module_clock_gating(CG_SSI2, CGM_ON_RUN_WAIT); + + /* Reset & disable SSIs */ + SSI_SCR1 &= ~SSI_SCR_SSIEN; + SSI_SCR2 &= ~SSI_SCR_SSIEN; + + SSI_SIER1 = 0; + SSI_SIER2 = 0; + + /* Set up audio mux */ + + /* Port 2 (internally connected to SSI2) + * All clocking is output sourced from port 4 */ + AUDMUX_PTCR2 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | + AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | + AUDMUX_PTCR_SYN; + + /* Receive data from port 4 */ + AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4; + /* All clock lines are inputs sourced from the master mode codec and + * sent back to SSI2 through port 2 */ + AUDMUX_PTCR4 = AUDMUX_PTCR_SYN; + + /* Receive data from port 2 */ + AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT2; + + /* PORT1 (internally connected to SSI1) routes clocking to PORT5 to + * provide MCLK to the codec */ + /* TX clocks are inputs taken from SSI2 */ + /* RX clocks are outputs taken from PORT4 */ + AUDMUX_PTCR1 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 | + AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4; + /* RX data taken from PORT4 */ + AUDMUX_PDCR1 = AUDMUX_PDCR_RXDSEL_PORT4; + + /* PORT5 outputs TCLK sourced from PORT1 (SSI1) */ + AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT1; + AUDMUX_PDCR5 = 0; + + /* Setup SSIs */ + + /* SSI2 - SoC software interface for all I2S data out */ + SSI_SCR2 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; + SSI_STCR2 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | + SSI_STCR_TEFS | SSI_STCR_TFEN0; + + /* 16 bits per word, 2 words per frame */ + SSI_STCCR2 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | + ((4-1) << SSI_STRCCR_PM_POS); + + /* Transmit low watermark */ + SSI_SFCSR2 = (SSI_SFCSR2 & ~SSI_SFCSR_TFWM0) | + ((8-SDMA_SSI_TXFIFO_WML) << SSI_SFCSR_TFWM0_POS); + SSI_STMSK2 = 0; + + /* SSI1 - provides MCLK to codec. Receives data from codec. */ + SSI_STCR1 = SSI_STCR_TXDIR; + + /* f(INT_BIT_CLK) = + * f(SYS_CLK) / [(DIV2 + 1)*(7*PSR + 1)*(PM + 1)*2] = + * 677737600 / [(1 + 1)*(7*0 + 1)*(0 + 1)*2] = + * 677737600 / 4 = 169344000 Hz + * + * 45.4.2.2 DIV2, PSR, and PM Bit Description states: + * Bits DIV2, PSR, and PM should not be all set to zero at the same + * time. + * + * The hardware seems to force a divide by 4 even if all bits are + * zero but comply by setting DIV2 and the others to zero. + */ + SSI_STCCR1 = SSI_STRCCR_DIV2 | ((1-1) << SSI_STRCCR_PM_POS); + + /* SSI1 - receive - asynchronous clocks */ + SSI_SCR1 = SSI_SCR_I2S_MODE_SLAVE; + + SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | + SSI_SRCR_REFS; + + /* 16 bits per word, 2 words per frame */ + SSI_SRCCR1 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | + ((4-1) << SSI_STRCCR_PM_POS); + + /* Receive high watermark */ + SSI_SFCSR1 = (SSI_SFCSR1 & ~SSI_SFCSR_RFWM0) | + (SDMA_SSI_RXFIFO_WML << SSI_SFCSR_RFWM0_POS); + SSI_SRMSK1 = 0; + + /* Enable SSI1 (codec clock) */ + SSI_SCR1 |= SSI_SCR_SSIEN; } diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c index 65571a4ee2..1c21415752 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c @@ -23,7 +23,7 @@ #include "kernel.h" #include "audio.h" #include "sound.h" -#include "ccm-imx31.h" +//#include "ccm-imx31.h" #include "sdma-imx31.h" #include "mmu-imx31.h" #include "pcm-internal.h" @@ -149,101 +149,11 @@ void pcm_dma_apply_settings(void) void pcm_play_dma_init(void) { - /* Init channel information */ + /* Init DMA channel information */ sdma_channel_init(DMA_PLAY_CH_NUM, &dma_play_cd, &dma_play_bd); sdma_channel_set_priority(DMA_PLAY_CH_NUM, DMA_PLAY_CH_PRIORITY); - ccm_module_clock_gating(CG_SSI1, CGM_ON_RUN_WAIT); - ccm_module_clock_gating(CG_SSI2, CGM_ON_RUN_WAIT); - - /* Reset & disable SSIs */ - SSI_SCR1 &= ~SSI_SCR_SSIEN; - SSI_SCR2 &= ~SSI_SCR_SSIEN; - - SSI_SIER1 = 0; - SSI_SIER2 = 0; - - /* Set up audio mux */ - - /* Port 2 (internally connected to SSI2) - * All clocking is output sourced from port 4 */ - AUDMUX_PTCR2 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | - AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | - AUDMUX_PTCR_SYN; - - /* Receive data from port 4 */ - AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4; - /* All clock lines are inputs sourced from the master mode codec and - * sent back to SSI2 through port 2 */ - AUDMUX_PTCR4 = AUDMUX_PTCR_SYN; - - /* Receive data from port 2 */ - AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT2; - - /* PORT1 (internally connected to SSI1) routes clocking to PORT5 to - * provide MCLK to the codec */ - /* TX clocks are inputs taken from SSI2 */ - /* RX clocks are outputs taken from PORT4 */ - AUDMUX_PTCR1 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 | - AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4; - /* RX data taken from PORT4 */ - AUDMUX_PDCR1 = AUDMUX_PDCR_RXDSEL_PORT4; - - /* PORT5 outputs TCLK sourced from PORT1 (SSI1) */ - AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT1; - AUDMUX_PDCR5 = 0; - - /* Setup SSIs */ - - /* SSI2 - SoC software interface for all I2S data out */ - SSI_SCR2 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; - SSI_STCR2 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | - SSI_STCR_TEFS | SSI_STCR_TFEN0; - - /* 16 bits per word, 2 words per frame */ - SSI_STCCR2 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | - ((4-1) << SSI_STRCCR_PM_POS); - - /* Transmit low watermark */ - SSI_SFCSR2 = (SSI_SFCSR2 & ~SSI_SFCSR_TFWM0) | - ((8-SDMA_SSI_TXFIFO_WML) << SSI_SFCSR_TFWM0_POS); - SSI_STMSK2 = 0; - - /* SSI1 - provides MCLK to codec. Receives data from codec. */ - SSI_STCR1 = SSI_STCR_TXDIR; - - /* f(INT_BIT_CLK) = - * f(SYS_CLK) / [(DIV2 + 1)*(7*PSR + 1)*(PM + 1)*2] = - * 677737600 / [(1 + 1)*(7*0 + 1)*(0 + 1)*2] = - * 677737600 / 4 = 169344000 Hz - * - * 45.4.2.2 DIV2, PSR, and PM Bit Description states: - * Bits DIV2, PSR, and PM should not be all set to zero at the same - * time. - * - * The hardware seems to force a divide by 4 even if all bits are - * zero but comply by setting DIV2 and the others to zero. - */ - SSI_STCCR1 = SSI_STRCCR_DIV2 | ((1-1) << SSI_STRCCR_PM_POS); - - /* SSI1 - receive - asynchronous clocks */ - SSI_SCR1 = SSI_SCR_I2S_MODE_SLAVE; - - SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | - SSI_SRCR_REFS; - - /* 16 bits per word, 2 words per frame */ - SSI_SRCCR1 = SSI_STRCCR_WL16 | ((2-1) << SSI_STRCCR_DC_POS) | - ((4-1) << SSI_STRCCR_PM_POS); - - /* Receive high watermark */ - SSI_SFCSR1 = (SSI_SFCSR1 & ~SSI_SFCSR_RFWM0) | - (SDMA_SSI_RXFIFO_WML << SSI_SFCSR_RFWM0_POS); - SSI_SRMSK1 = 0; - - /* Enable SSI1 (codec clock) */ - SSI_SCR1 |= SSI_SCR_SSIEN; - + /* Init audio interfaces */ audiohw_init(); } diff --git a/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c index 36ab33a5dc..ca23aa4e56 100644 --- a/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c @@ -22,24 +22,93 @@ ****************************************************************************/ #include "config.h" #include "system.h" -#include "kernel.h" -#include "sound.h" +#include "audiohw.h" #include "wmcodec.h" +#include "audio.h" #include "i2s.h" #include "i2c-imx31.h" -/* NOTE: Some port-specific bits will have to be moved away (node and GPIO - * writes) for cleanest implementation. */ - static struct i2c_node wm8978_i2c_node = { .num = I2C1_NUM, .ifdr = I2C_IFDR_DIV192, /* 66MHz/.4MHz = 165, closest = 192 = 343750Hz */ - /* Just hard-code for now - scaling may require - * updating */ .addr = WMC_I2C_ADDR, }; +/* For 16.9344MHz MCLK, codec as master. */ +const struct wmc_srctrl_entry wmc_srctrl_table[HW_NUM_FREQ] = +{ + [HW_FREQ_8] = /* PLL = 65.536MHz */ + { + .plln = 7 | WMC_PLL_PRESCALE, + .pllk1 = 0x2f, /* 12414886 */ + .pllk2 = 0x0b7, + .pllk3 = 0x1a6, + .mclkdiv = WMC_MCLKDIV_8, /* 2.0480 MHz */ + .filter = WMC_SR_8KHZ, + }, + [HW_FREQ_11] = /* PLL = off */ + { + .mclkdiv = WMC_MCLKDIV_6, /* 2.8224 MHz */ + .filter = WMC_SR_12KHZ, + }, + [HW_FREQ_12] = /* PLL = 73.728 MHz */ + { + .plln = 8 | WMC_PLL_PRESCALE, + .pllk1 = 0x2d, /* 11869595 */ + .pllk2 = 0x08e, + .pllk3 = 0x19b, + .mclkdiv = WMC_MCLKDIV_6, /* 3.0720 MHz */ + .filter = WMC_SR_12KHZ, + }, + [HW_FREQ_16] = /* PLL = 65.536MHz */ + { + .plln = 7 | WMC_PLL_PRESCALE, + .pllk1 = 0x2f, /* 12414886 */ + .pllk2 = 0x0b7, + .pllk3 = 0x1a6, + .mclkdiv = WMC_MCLKDIV_4, /* 4.0960 MHz */ + .filter = WMC_SR_16KHZ, + }, + [HW_FREQ_22] = /* PLL = off */ + { + .mclkdiv = WMC_MCLKDIV_3, /* 5.6448 MHz */ + .filter = WMC_SR_24KHZ, + }, + [HW_FREQ_24] = /* PLL = 73.728 MHz */ + { + .plln = 8 | WMC_PLL_PRESCALE, + .pllk1 = 0x2d, /* 11869595 */ + .pllk2 = 0x08e, + .pllk3 = 0x19b, + .mclkdiv = WMC_MCLKDIV_3, /* 6.1440 MHz */ + .filter = WMC_SR_24KHZ, + }, + [HW_FREQ_32] = /* PLL = 65.536MHz */ + { + .plln = 7 | WMC_PLL_PRESCALE, + .pllk1 = 0x2f, /* 12414886 */ + .pllk2 = 0x0b7, + .pllk3 = 0x1a6, + .mclkdiv = WMC_MCLKDIV_2, /* 8.1920 MHz */ + .filter = WMC_SR_32KHZ, + }, + [HW_FREQ_44] = /* PLL = off */ + { + .mclkdiv = WMC_MCLKDIV_1_5, /* 11.2896 MHz */ + .filter = WMC_SR_48KHZ, + }, + [HW_FREQ_48] = /* PLL = 73.728 MHz */ + { + .plln = 8 | WMC_PLL_PRESCALE, + .pllk1 = 0x2d, /* 11869595 */ + .pllk2 = 0x08e, + .pllk3 = 0x19b, + .mclkdiv = WMC_MCLKDIV_1_5, /* 12.2880 MHz */ + .filter = WMC_SR_48KHZ, + }, +}; + void audiohw_init(void) { i2s_reset(); |