summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2014-09-13 15:43:46 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2014-09-13 15:43:46 +0200
commit3456c9e10432dd70cd1ec2f82a6a3cc19f009a15 (patch)
tree2e49d119df949d8d571df7c2145d75f65503028d
parent5a4b6a5e6979fa034d537a803d43c04181a5ade7 (diff)
Some I2S interface cleaning up.
-rw-r--r--firmware/drivers/i2s.c108
-rw-r--r--firmware/drivers/i2s.h172
2 files changed, 194 insertions, 86 deletions
diff --git a/firmware/drivers/i2s.c b/firmware/drivers/i2s.c
index 7c87133..df32801 100644
--- a/firmware/drivers/i2s.c
+++ b/firmware/drivers/i2s.c
@@ -26,24 +26,16 @@
*/
#include "i2s.h"
-static void i2s_set_power(int power)
+void i2s_set_power(int power)
{
// 1. Enable I²S in PCONP register. Set PCI2S to 1 (see table 46, pg. 64)
// Remark: On reset, the I²S interface is disabled (PCI2S = 0).
uint32_t *pconp = (uint32_t *)0x400FC0C4;
*pconp &= ~(0b1 << 27); // Clear bit
- *pconp |= ~( (power & 0b1) << 27); // Set bit
+ *pconp |= ~( ((power != 0) & 0b1) << 27); // Set bit
}
-typedef enum {
- I2S_CCLK_4 = 0b00, // PCLK_peripheral = CCLK/4
- I2S_CCLK = 0b01, // PCLK_peripheral = CCLK
- I2S_CCLK_2 = 0b10, // PCLK_peripheral = CCLK/2
- I2S_CCLK_8 = 0b11, // PCLK_peripheral = CCLK/8, except for CAN1, CAN2, and
- // CAN filtering when 0b11 selects = CCLK/6.
-} i2s_clksel_t;
-
-static void i2s_set_clksel(i2s_clksel_t sel)
+void i2s_set_clksel(i2s_clksel_t sel)
{
// 2. Clock: In PCLKSEL1 select PCLK_I2S. (see table 41, pg. 57)
uint32_t *pclksel1 = (uint32_t*)0x400FC1AC;
@@ -51,7 +43,7 @@ static void i2s_set_clksel(i2s_clksel_t sel)
*pclksel1 |= ( (sel & 0b11) << 22); // Set bits
}
-static void i2s_set_pinsel()
+void i2s_set_pinsel()
{
//3. Pins: Select I²S pins and their modes in PINSEL0 to PINSEL4 and PINMODE0
// to PINMODE4 (see Section 8.5).
@@ -62,11 +54,11 @@ static void i2s_set_pinsel()
// Reset ports
*pinsel0 &=
~(
- /* // We only do TX
+ // TX:
(0b11 << 8) | // P0.4
(0b11 << 10) | // P0.5
(0b11 << 12) | // P0.6
- */
+ // RX:
(0b11 << 14) | // P0.7
(0b11 << 16) | // P0.8
(0b11 << 18)) // P0.9
@@ -75,11 +67,11 @@ static void i2s_set_pinsel()
// Set ports
*pinsel0 |=
(
- /* // We only do TX
+ // TX:
(0b01 << 8) | // P0.4: I2SRX_CLK
(0b01 << 10) | // P0.5: I2SRX_WS
(0b01 << 12) | // P0.6: I2SRX_SDA
- */
+ // RX:
(0b01 << 14) | // P0.7: I2STX_CLK
(0b01 << 16) | // P0.8: I2STX_WS
(0b01 << 18)) // P0.9: I2STX_SDA
@@ -99,23 +91,7 @@ static void i2s_set_pinsel()
;
}
-typedef enum {
- WW_8_BIT = 0b00, // 8-bit data
- WW_16_BIT = 0b01, // 16-bit data
- WW_RESERVED = 0b10, // Reserved, do not use this setting
- WW_32_BIT = 0b11, // 32-bit data
-} wordwidth_t;
-
-typedef enum {
- CH_MONO = 0b1, // Data is mono format
- CH_STEREO = 0b0, // Data is stereo format
-} channels_t;
-
-/**
- * Sets DAO register values and stops/resets the bus.
- */
-static void i2s_set_dao_register(wordwidth_t ww,
- channels_t ch)
+void i2s_set_dao_register(wordwidth_t ww, channels_t ch)
{
// See table 405, pg. 476
uint32_t *i2sdao = (uint32_t*)0x400A8000;
@@ -130,12 +106,7 @@ static void i2s_set_dao_register(wordwidth_t ww,
;
}
-/*
-**
- * Sets DAI register values and stops/resets the bus.
- *
-static void i2s_set_dai_register(wordwidth_t ww,
- channels_t ch)
+void i2s_set_dai_register(wordwidth_t ww, channels_t ch)
{
// See table 406, pg. 477
uint32_t *i2sdai = (uint32_t*)0x400A8004;
@@ -149,53 +120,36 @@ static void i2s_set_dai_register(wordwidth_t ww,
(0b0 << 15) // no mute
;
}
-*/
-/**
- * I2STXMCLK = PCLK_I2S * (X/Y) /2
- */
-static void i2s_set_tx_rate(uint8_t y_div, uint8_t x_div)
+void i2s_set_tx_rate(uint8_t y_div, uint8_t x_div)
{
// See table 413, pg. 480
uint32_t *i2stxrate = (uint32_t*)0x400A8020;
*i2stxrate = y_div | (x_div << 8);
}
-/*
-static void i2s_set_rx_rate(uint8_t y_div, uint8_t x_div)
+void i2s_set_rx_rate(uint8_t y_div, uint8_t x_div)
{
// See table 414, pg. 481
uint32_t *i2srxrate = (uint32_t*)0x400A8024;
*i2srxrate = y_div | (x_div << 8);
}
-*/
-/**
- * I²S transmit bit rate. This value plus one is used to divide TX_MCLK to
- * produce the transmit bit clock.
- */
-static void i2s_set_tx_clock_bitrate(uint8_t bitrate)
+void i2s_set_tx_clock_bitrate(uint8_t bitrate)
{
// See table 415, pg. 481
uint32_t *i2stxbitrate = (uint32_t*)0x400A8028;
*i2stxbitrate = (bitrate & 0b111111);
}
-/*
-static void i2s_set_rx_clock_bitrate(uint8_t bitrate)
+void i2s_set_rx_clock_bitrate(uint8_t bitrate)
{
// See table 416, pg. 481
uint32_t *i2srxbitrate = (uint32_t*)0x400A802C;
*i2srxbitrate = (bitrate & 0b111111);
}
-*/
-
-typedef enum {
- CLK_TX_SRC = 0b00, // Select the TX fractional rate divider clock output as the source
- CLK_RX_MCLK = 0b10, // Select the RX_MCLK signal as the TX_MCLK clock source
-} clksel_t;
-static void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena)
+void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena)
{
// See table 417, pg 482
uint32_t *i2stxmode = (uint32_t*)0x400A8030;
@@ -205,19 +159,17 @@ static void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena)
;
}
-/*
-static void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena)
+void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena)
{
// See table 418, pg 483
uint32_t *i2srxmode = (uint32_t*)0x400A8034;
- *i2stxmode = c |
+ *i2srxmode = c |
((_4pin & 0b1) << 2) |
((mcena & 0b1) << 3)
;
}
-*/
-static void i2s_tx_reset()
+void i2s_tx_reset()
{
// See table 405, pg. 476
uint32_t *i2sdao = (uint32_t*)0x400A8000;
@@ -245,8 +197,7 @@ void i2s_tx_start()
;
}
-/*
-static void i2s_rx_reset()
+void i2s_rx_reset()
{
// See table 406, pg. 477
uint32_t *i2sdai = (uint32_t*)0x400A8004;
@@ -254,10 +205,8 @@ static void i2s_rx_reset()
(0b1 << 4) // reset
;
}
-*/
-/*
-static void i2s_rx_stop()
+void i2s_rx_stop()
{
// See table 406, pg. 477
uint32_t *i2sdai = (uint32_t*)0x400A8004;
@@ -265,9 +214,8 @@ static void i2s_rx_stop()
(0b1 << 3) // stop bit
;
}
-*/
-/*
-static void i2s_rx_start()
+
+void i2s_rx_start()
{
// See table 406, pg. 477
uint32_t *i2sdai = (uint32_t*)0x400A8004;
@@ -275,18 +223,6 @@ static void i2s_rx_start()
~(0b1 << 3) // stop bit
;
}
- */
-/*
- * A data sample in the FIFO consists of:
- * - 1×32 bits in 8-bit or 16-bit stereo modes.
- * - 1×32 bits in mono modes.
- * - 2×32 bits, first left data, second right data, in 32-bit stereo modes.
- *
- * I2STXFIFO: 0x400A 8008 - 8 x 32 bit
- * I2SRXFIFO: 0x400A 800C - 8 x 32 bit
- *
- * See figure 113, pg. 489, for examples.
- */
/**
* This bit reflects the presence of Receive Interrupt or Transmit Interrupt.
diff --git a/firmware/drivers/i2s.h b/firmware/drivers/i2s.h
index 6e3acf2..386476e 100644
--- a/firmware/drivers/i2s.h
+++ b/firmware/drivers/i2s.h
@@ -29,8 +29,180 @@
#include <stdint.h>
+/**
+ * Set power of I²S circuit.
+ * @param power Integer value 0 is off, nonzero values are on.
+ */
+void i2s_set_power(int power);
+
+/**
+ * Clock selection mode. See table 41, pg. 57.
+ */
+typedef enum {
+ I2S_CCLK_4 = 0b00, ///< PCLK_peripheral = CCLK/4
+ I2S_CCLK = 0b01, ///< PCLK_peripheral = CCLK
+ I2S_CCLK_2 = 0b10, ///< PCLK_peripheral = CCLK/2
+ I2S_CCLK_8 = 0b11, ///< PCLK_peripheral = CCLK/8
+} i2s_clksel_t;
+
+#define I2S_CCLK_6 ((i2s_clksel_t)0b11) ///< For CAN1, CAN2, and CAN filtering when 0b11 selects = CCLK/6.
+
+/**
+ * Set clock selection for I²S circuit.
+ * @param sel Clock selection mode.
+ */
+void i2s_set_clksel(i2s_clksel_t sel);
+
+/**
+ * Set pin selection for I²S circuit.
+ * RX:
+ * - P0.4: I2SRX_CLK
+ * - P0.5: I2SRX_WS
+ * - P0.6: I2SRX_SDA
+ * - P?.?: RX_MCLK (currently not set)
+ * TX:
+ * - P0.7: I2STX_CLK
+ * - P0.8: I2STX_WS
+ * - P0.9: I2STX_SDA
+ * - P4.28: TX_MCLK
+ */
+void i2s_set_pinsel();
+
+typedef enum {
+ WW_8_BIT = 0b00, // 8-bit data
+ WW_16_BIT = 0b01, // 16-bit data
+ // WW_RESERVED = 0b10, // Reserved, do not use this setting
+ WW_32_BIT = 0b11, // 32-bit data
+} wordwidth_t;
+
+typedef enum {
+ CH_MONO = 0b1, // Data is mono format
+ CH_STEREO = 0b0, // Data is stereo format
+} channels_t;
+
+/**
+ * Sets DAO register values and stops/resets the bus.
+ * @param ww Word width of pcm values.
+ * @param ch Number of channels (mono/stereo)
+ */
+void i2s_set_dao_register(wordwidth_t ww, channels_t ch);
+
+/**
+ * Sets DAI register values and stops/resets the bus.
+ * @param ww Word width of pcm values.
+ * @param ch Number of channels (mono/stereo)
+ */
+void i2s_set_dai_register(wordwidth_t ww, channels_t ch);
+
+/**
+ * Set clock transmit rate as PCLK_I2S * (X/Y) / 2
+ * @param y_div I²S transmit MCLK rate denominator. This value is used to
+ * divide PCLK to produce the transmit MCLK. Eight bits of fractional divide
+ * supports a wide range of possibilities. A value of 0 stops the clock.
+ * @param x_div I²S transmit MCLK rate numerator. This value is used to
+ * multiply PCLK by to produce the transmit MCLK. A value of 0 stops the clock.
+ * Eight bits of fractional divide supports a wide range of possibilities.
+ * Note: the resulting ratio X/Y is divided by 2.
+ */
+void i2s_set_tx_rate(uint8_t y_div, uint8_t x_div);
+
+/**
+ * Set clock receive rate as PCLK_I2S * (X/Y) / 2
+ * @param y_div I²S receive MCLK rate denominator. This value is used to divide
+ * PCLK to produce the receive MCLK. Eight bits of fractional divide supports
+ * a wide range of possibilities. A value of 0 stops the clock.
+ * @param x_div I²S receive MCLK rate numerator. This value is used to multiply
+ * PCLK by to produce the receive MCLK. A value of 0 stops the clock. Eight
+ * bits of fractional divide supports a wide range of possibilities.
+ * Note: the resulting ratio X/Y is divided by 2.
+ */
+void i2s_set_rx_rate(uint8_t y_div, uint8_t x_div);
+
+/**
+ * I²S transmit bit rate.
+ * @param bitrate This value plus one is used to divide TX_MCLK to produce the
+ * transmit bit clock.
+ */
+void i2s_set_tx_clock_bitrate(uint8_t bitrate);
+
+/**
+ * I²S receive bit rate.
+ * @param bitrate This value plus one is used to divide RX_MCLK to produce the
+ * receive bit clock.
+ */
+void i2s_set_rx_clock_bitrate(uint8_t bitrate);
+
+typedef enum {
+ CLK_TX_SRC = 0b00, ///< Select the TX fractional rate divider clock output as the source
+ CLK_RX_MCLK = 0b10, ///< Select the RX_MCLK signal as the TX_MCLK clock source
+} clksel_t;
+
+/**
+ *
+ */
+void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena);
+
+/**
+ *
+ */
+void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena);
+
+void i2s_tx_reset();
+void i2s_tx_stop();
+void i2s_tx_start();
+
+void i2s_rx_reset();
+void i2s_rx_stop();
+void i2s_rx_start();
+
+/*
+ * A data sample in the FIFO consists of:
+ * - 1×32 bits in 8-bit or 16-bit stereo modes.
+ * - 1×32 bits in mono modes.
+ * - 2×32 bits, first left data, second right data, in 32-bit stereo modes.
+ *
+ * I2STXFIFO: 0x400A 8008 - 8 x 32 bit (see table 407, pg 477)
+ * I2SRXFIFO: 0x400A 800C - 8 x 32 bit (see table 408, pg 478)
+ *
+ * See figure 113, pg. 489, for examples.
+ */
+
+/**
+ * Write to TXFIFO
+ * I2STXFIFO: 0x400A 8008 - 8 x 32 bit (see table 407, pg 477)
+ */
+#define TF(t, n) t *n = (t*)0x400A8008
+inline void i2s_write_pcm_8_mono(int8_t s) { TF(int8_t, t); t[0] = s; }
+inline void i2s_write_pcm_16_mono(int16_t s) { TF(int16_t, t); t[0] = s; }
+inline void i2s_write_pcm_32_mono(int32_t s) { TF(int32_t, t); t[0] = s; }
+inline void i2s_write_pcm_8_stereo(int8_t l, int8_t r)
+{ TF(int8_t, t); t[0] = l; t[1] = r; }
+inline void i2s_write_pcm_16_stereo(int16_t l, int16_t r)
+{ TF(int16_t, t); t[0] = l; t[1] = r; }
+inline void i2s_write_pcm_32_stereo(int32_t l, int32_t r)
+{ TF(int32_t, t); t[0] = l; t[1] = r; }
+
+/**
+ * Read from RXFIFO
+ * I2SRXFIFO: 0x400A 800C - 8 x 32 bit (see table 408, pg 478)
+ */
+#define RF(t, n) t *n = (t*)0x400A800C
+inline void i2s_read_pcm_8_mono(int8_t *s) { RF(int8_t, t); *s = t[0]; }
+inline void i2s_read_pcm_16_mono(int16_t *s) { RF(int16_t, t); *s = t[0]; }
+inline void i2s_read_pcm_32_mono(int32_t *s) { RF(int32_t, t); *s = t[0]; }
+inline void i2s_read_pcm_8_stereo(int8_t *l, int8_t *r)
+{ RF(int8_t, t); *l = t[0]; *r = t[1]; }
+inline void i2s_read_pcm_16_stereo(int16_t *l, int16_t *r)
+{ RF(int16_t, t); *l = t[0]; *r = t[1]; }
+inline void i2s_read_pcm_32_stereo(int32_t *l, int32_t *r)
+{ RF(int32_t, t); *l = t[0]; *r = t[1]; }
+
+/**
+ * Convenience function for power, pin, clock and register configuration.
+ */
void i2s_init();
+
/**
* Enables accesses on FIFOs, places the transmit channel in unmute mode.
*/