summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/drivers/audio/wm8978.c95
-rw-r--r--firmware/export/wm8978.h11
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/i2s-gigabeat-s.c93
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c96
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/wmcodec-gigabeat-s.c83
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();