From e1f7dada40559a47f4496381426f71c88cd9605b Mon Sep 17 00:00:00 2001
From: Bent Bisballe Nyeng <deva@aasimon.org>
Date: Tue, 15 Jul 2014 18:36:40 +0200
Subject: Almost complete wm8523 api. Initial experiments with I2S.

---
 firmware/drivers/wm8523.c | 403 ++++++++++++++++++++++++++++++++++++++++------
 firmware/drivers/wm8523.h | 349 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 700 insertions(+), 52 deletions(-)

(limited to 'firmware/drivers')

diff --git a/firmware/drivers/wm8523.c b/firmware/drivers/wm8523.c
index f8cad99..96d5cae 100644
--- a/firmware/drivers/wm8523.c
+++ b/firmware/drivers/wm8523.c
@@ -26,74 +26,381 @@
  */
 #include "wm8523.h"
 
-#if 1
+#include <ssp.h>
+#include <GPIO.h>
+#include <i2s.h>
 
-#include <string.h>
-#include <cli.h>
+#define READ_BIT  0b1
+#define WRITE_BIT 0b0
 
-#include "spi.h"
+// Registers:
+#define R0 0
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
 
-#define WM8523_WRITE 0
-#define WM8523_READ 1
+typedef struct __attribute__ ((packed)) {
+  uint8_t reg:7;
+  uint8_t rw:1;
+  uint8_t msbyte;
+  uint8_t lsbyte;
+} _reg_t; 
 
-typedef union {
-  struct __attribute__ ((packed)) {
-    uint8_t rw:1; ///< See WM8523_WRITE and WM8523_READ
-    uint8_t reg:7; ///< See page 35 in the WM8523 manual.
-    uint16_t data;
-  } val;
-  uint8_t data[3];
-} WM8523_transfer_t;
+static void reg_read(uint8_t portnum, char reg, uint8_t *buf, int size)
+{
+  _reg_t data;
+  data.rw = READ_BIT;
+  data.reg = reg;
+
+	GPIOSetValue(0, 16, 0);
+	SSPSend(portnum, (uint8_t *)&data, 1); // write register number and read bit
+	SSPReceive(portnum, (uint8_t *)&data.msbyte, 2); // read 16bit register value
+	GPIOSetValue(0, 16, 1);
 
-void WM8523_init()
-{  
-  //cli_write("sizeof: %d", sizeof(WM8523_transfer_t));
-  spi_init();
+  if(size == 1) {
+    buf[0] = data.lsbyte;
+  } else if(size == 2) {
+    buf[0] = data.lsbyte;
+    buf[1] = data.msbyte;
+  } else {
+    // Error
+    return;
+  }
 }
 
-void WM8523_deinit()
+static void reg_write(uint8_t portnum, char reg, const uint8_t *buf, int size)
 {
-  spi_deinit();
+  _reg_t data;
+  data.rw = WRITE_BIT;
+  data.reg = reg;
+  switch(size) {
+  case 0:
+    data.lsbyte = 0;
+    data.msbyte = 0;
+    break;
+  case 1:
+    data.lsbyte = buf[0];
+    data.msbyte = 0;
+    break;
+  case 2:
+    data.lsbyte = buf[0];
+    data.msbyte = buf[1];
+    break;
+  default:
+    // Error...
+    return;
+  }
+
+	GPIOSetValue(0, 16, 0);
+	SSPSend(portnum, (uint8_t *)&data, sizeof(data));
+	GPIOSetValue(0, 16, 1);
 }
 
-void WM8523_write(uint8_t reg, uint16_t data)
+/**
+ * Prepare I2S, SSP and GPIO.
+ */
+void wm8523_init(uint8_t portnum, wm8523_samplerate_t fs)
 {
-  WM8523_transfer_t t;
-  t.val.rw = WM8523_WRITE;
-  t.val.reg = reg;
-  t.val.data = data;
+	// Init SPI
+	GPIOSetDir(0, 16, 1);
+	GPIOSetValue(0, 16, 1);
+
+  if(portnum == 0) SSP0Init();
+  else SSP1Init();
+
+  I2SInit();
+
+  // pg. 482
+  // I2STXMODE 0x400A 8030
+  //  bit 3  Enable for the TX_MCLK output. When 0, output of TX_MCLK is not enabled. When 1, output of TX_MCLK is enabled.
+  // set to 1, default is 0
+  uint32_t *i2stxmode = (uint32_t*)0x400a8030;
+  *i2stxmode |=
+    (0b1 << 3) // Enable TX_MCLK output.
+    ;
+
+  // pg. 477
+  // I2SDAO 0x400A 8000
+  // bit 5  When 0, the interface is in master mode. When 1, the interface is in slave mode.
+  // set to 0, default is 1
+  uint32_t *i2sdao = (uint32_t*)0x400a8030;
+  *i2sdao &=
+    ~(0b1 << 5) // Set I2S in master mode.
+    ;
+
+
+  // pg. 481
+  // I2STXBITRATE 0x400A 8028
+  // bit 0-5  I2S transmit bit rate. This value plus one is used to divide TX_MCLK to produce the transmit bit clock.
+  // set to 31 (divide by 32): stereo (2) * 16bit
+  uint32_t *i2stxbirate = (uint32_t *)0x400a8028;
+  *i2stxbirate = 7;
 
-  WM8523_transfer_t r;
-  r.val.data = 0xffff;
+  // bitclock = mclock / (divider+1)
+  // samplerate = mclock / bitclock
 
-  spi_read_write(t.data, r.data, 3);
+
+  (void)fs;
+  /*
+  WM8523_FS_8K,
+  WM8523_FS_32K,
+  WM8523_FS_44K1,
+  WM8523_FS_48K,
+  WM8523_FS_88K2,
+  WM8523_FS_96K,
+  WM8523_FS_176K4,
+  WM8523_FS_192K,
+  */
+  uint32_t *pclksel1 = (uint32_t *)0x400fc1ac;
+  *pclksel1 |= 
+    (0b10 << 22) // Select I2S clock: PCLK/2 see pg. 57 table 41/42
+    ;
+
+  uint32_t *pinsel9 = (uint32_t *)0x4002c024;
+  *pinsel9 |=
+    (0b01 << 26) // Select p4.29 as TX_MCLK
+    ;
+
+  /*
+  uint32_t *pinsel3 = (uint32_t *)0x4002c00c;
+  *pinsel3 |=
+    (0b01 << 22) // config p1.27 as CLKOUT 
+    ;
+
+  // Set up master clock see pg. 66
+  uint32_t *clkcfg = (uint32_t *)0x400fc1c8;
+  *clkcfg =
+    (0b0000 << 0) | // Select RTC as clock source
+    (0b1111 << 4) | // Divide by 16
+    (0b1    << 8)   // Enable
+    ;
+  */    
 }
 
-uint16_t WM8523_read(uint8_t reg)
+unsigned short wm8523_get_chip_id(uint8_t portnum)
 {
-  WM8523_transfer_t t;
-  t.val.rw = WM8523_READ;
-  t.val.reg = reg;
-  t.val.data = 0xffff;
-  
-  WM8523_transfer_t r;
+  unsigned short id;
+  reg_read(portnum, R0, (uint8_t*)&id, sizeof(id));
+  return id;
+}
 
-  spi_read_write(t.data, r.data, 3);
-  
-  return r.data[0] | (r.data[1] << 8);//r.val.data;
+void wm8523_reset_registers(uint8_t portnum)
+{
+  reg_write(portnum, R0, 0, 0);
 }
 
-/**
-Volume update registers R06h and R07h are unavailable in SPI control mode.
-To use volume update in software control mode, I2C mode must be used.
-*/
-void WM8523_configure()
+uint8_t wm8523_get_hardware_revision(uint8_t portnum)
+{
+  uint8_t rev;
+  reg_read(portnum, R1, &rev, sizeof(rev));
+  return rev;
+}
+
+wm8523_power_mode_t wm8523_get_power_mode(uint8_t portnum)
+{
+  wm8523_power_mode_t mode;
+  reg_read(portnum, R2, (uint8_t*)&mode, sizeof(mode));
+  return mode;
+}
+
+void wm8523_set_power_mode(uint8_t portnum, wm8523_power_mode_t mode)
+{
+  reg_write(portnum, R2, (uint8_t*)&mode, sizeof(mode));
+}
+
+void wm8523_set_aif_ctrl1(uint8_t portnum, wm8523_aif_ctrl1_t aif_ctrl1)
+{
+  aif_ctrl1.blank = aif_ctrl1.reserved = 0;
+  reg_write(portnum, R3, (uint8_t*)&aif_ctrl1, sizeof(aif_ctrl1));
+}
+
+wm8523_aif_ctrl1_t wm8523_get_aif_ctrl1(uint8_t portnum)
+{
+  wm8523_aif_ctrl1_t aif_ctrl1;
+
+  reg_read(portnum, R3, (uint8_t*)&aif_ctrl1, sizeof(aif_ctrl1));
+
+  return aif_ctrl1;
+}
+
+void wm8523_set_aif_ctrl2(uint8_t portnum, wm8523_aif_ctrl2_t aif_ctrl2)
+{
+  reg_write(portnum, R4, (uint8_t*)&aif_ctrl2, sizeof(aif_ctrl2));
+}
+
+wm8523_aif_ctrl2_t wm8523_get_aif_ctrl2(uint8_t portnum)
+{
+  wm8523_aif_ctrl2_t aif_ctrl2;
+
+  reg_read(portnum, R4, (uint8_t*)&aif_ctrl2, sizeof(aif_ctrl2));
+
+  return aif_ctrl2;
+}
+
+void wm8523_set_dac_ctrl3(uint8_t portnum, wm8523_dac_ctrl3_t dac_ctrl3)
+{
+  reg_write(portnum, R5, (uint8_t*)&dac_ctrl3, sizeof(dac_ctrl3));
+}
+
+wm8523_dac_ctrl3_t wm8523_get_dac_ctrl3(uint8_t portnum)
+{
+  wm8523_dac_ctrl3_t dac_ctrl3;
+
+  reg_read(portnum, R5, (uint8_t*)&dac_ctrl3, sizeof(dac_ctrl3));
+
+  return dac_ctrl3;
+}
+
+#include "dma.h"
+#include <LPC17xx.h>
+
+extern volatile uint8_t *I2STXBuffer, *I2SRXBuffer;
+extern volatile uint32_t I2SReadLength;
+extern volatile uint32_t I2SWriteLength;
+extern volatile uint32_t I2SRXDone, I2STXDone;
+extern volatile uint32_t I2SDMA0Done, I2SDMA1Done;
+
+short signal(int x)
 {
-  // spi_init();
+  static short v = 0;
+  if((x / 1000) % 2) v++;
+  else v--;
+  return x % 0xffff;
+}
+
+#if 0
+//
+// Sine approximation using taylor series
+// http://en.wikipedia.org/wiki/Taylor_series
+//
+#define M_PI 3.14159265359
+float expand(float x, int e)
+{
+  
+  int fak = 1;
+  while(e) {
+    x *= x;
+    fak *= e;
+    e--;
+  }
 
-  uint16_t id = WM8523_read(0); // Read chip id from reg0.
-  (void)id;
-  // cli_write("=%d=", id); // should be 34595 (0x8523)
+  return x / (float)fak;
 }
 
-#endif/*0*/
+float _sin(float x)
+{
+  return x - expand(x, 3) + expand(x, 5) - expand(x, 7);
+}
+#endif
+
+#include "../src/sample.h"
+
+void wm8523_tone()
+{
+  // I2SSTATE 0x400A 8010
+  // bit 16-19  Reflects the current level of the Transmit FIFO.
+  uint32_t *i2sstate = (uint32_t *)0x400a8010;
+  (void)i2sstate;
+
+  // I2STXFIFO 0x400A 8008
+  // 8 × 32-bit transmit FIFO.
+  uint32_t *i2stxfifo = (uint32_t*)0x400a8008;
+
+  // Not DMA mode, enable I2S interrupts.
+  NVIC_EnableIRQ(I2S_IRQn);
+
+  // RX FIFO depth is 1, TX FIFO depth is 8.
+  I2SStart();
+  LPC_I2S->I2SIRQ = (8 << 16) | (1 << 8) | (0x01 << 0);
+
+  //uint32_t val = 0;
+  uint32_t cnt = 0;
+  while(1) {
+    while((*i2sstate & (0b1111 << 16)) != 0) { }
+    
+    *i2stxfifo = samples[(cnt ++) % sizeof(samples)];
+  }
+
+
+#if 0
+  int i;
+
+  short *pcm = (short*)I2STXBuffer;
+
+  /// Configure temp register before reading
+  int pcm_size = BUFSIZE / sizeof(short) / 2; // 2 channels
+  for ( i = 0; i < pcm_size; i++ )	{ // Clear buffer
+    //float s = _sin((float)i / 44100 * 2 * M_PI * 440);
+    short s = signal(i);
+    pcm[2 * i] = s * 1234567890;//s * 32000;//samples[i];
+    pcm[2 * i + 1] = s * 1234567890;//s * 32000;//samples[i];
+    //I2SRXBuffer[i] = 0;
+  }
+
+  //  I2SInit();				// Initialize I2S
+
+ #if I2S_DMA_ENABLED
+  DMA_Init();
+
+  // Select secondary function(I2S) in DMA channels 
+  LPC_SC->DMAREQSEL = (0x1<<DMA_I2S_REQ0)|(0x1<<DMA_I2S_REQ1);
+
+  // On DMA channel 0, Source is memory, destination is I2S TX FIFO, 
+  // On DMA channel 1, source is I2S RX FIFO, Destination is memory
+
+  // Enable channel and IE bit
+  DMAChannel_Init( 0, M2P );
+  LPC_GPDMACH0->DMACCConfig |= (0x18001|(0x00<<1)|(DMA_I2S_REQ0<<6)|(0x01<<11));
+
+  //DMAChannel_Init( 1, P2M );
+  //  LPC_GPDMACH1->CConfig |= (0x08001|(DMA_I2S_REQ1<<1)|(0x00<<6)|(0x02<<11));
+ 
+  NVIC_EnableIRQ(DMA_IRQn);
+
+  I2SStart();	  
+
+  // Channel 2 is for RX, enable RX first.
+  //LPC_I2S->I2SDMA2 = (0x01<<0) | (0x08<<8);
+	
+  // Channel 1 is for TX.
+  LPC_I2S->I2SDMA1 = (0x01<<1) | (0x01<<16);
+
+  // Wait for both DMA0 and DMA1 to finish before verifying.
+  while(!I2SDMA0Done/* || !I2SDMA1Done*/);
+#else
+
+  //  this does not compile
+
+  // Not DMA mode, enable I2S interrupts.
+  NVIC_EnableIRQ(I2S_IRQn);
+
+  // RX FIFO depth is 1, TX FIFO depth is 8.
+  I2SStart();
+  LPC_I2S->I2SIRQ = (8 << 16) | (1 << 8) | (0x01 << 0);
+
+  while(I2SWriteLength < BUFSIZE) {
+    while (((LPC_I2S->I2SSTATE >> 16) & 0xFF) == TXFIFO_FULL);
+    LPC_I2S->I2STXFIFO = I2STXBuffer[I2SWriteLength++];
+  }
+
+  I2STXDone = 1;
+
+  // Wait for RX and TX complete before comparison
+  while(!I2SRXDone || !I2STXDone);
+#endif
+  /*
+  // Validate TX and RX buffer
+  for(i=1; i<BUFSIZE; i++) {
+    if(I2SRXBuffer[i] != I2STXBuffer[i-1]) {
+      while(1);	// Validation error
+    }
+  }
+  while(1);	// Don't exit from main when finishing.
+  */
+#endif
+}
diff --git a/firmware/drivers/wm8523.h b/firmware/drivers/wm8523.h
index e1bb8bc..aeffe7b 100644
--- a/firmware/drivers/wm8523.h
+++ b/firmware/drivers/wm8523.h
@@ -29,9 +29,350 @@
 
 #include <stdint.h>
 
-void WM8523_init();
-void WM8523_configure();
-uint16_t WM8523_read(uint8_t reg);
-void WM8523_write(uint8_t reg, uint16_t data);
+
+typedef enum {
+  WM8523_FS_8K,
+  WM8523_FS_32K,
+  WM8523_FS_44K1,
+  WM8523_FS_48K,
+  WM8523_FS_88K2,
+  WM8523_FS_96K,
+  WM8523_FS_176K4,
+  WM8523_FS_192K,
+} wm8523_samplerate_t;
+/**
+ * Prepare I2S, SSP and GPIO.
+ */
+void wm8523_init(uint8_t portnum, wm8523_samplerate_t fs);
+
+/**
+ * Read chip ID.
+ * Is always 0x8523
+ * Register: R0 [0:15] (read)
+ * Default: 1000 0101 0010 0011 
+ */
+#define WM8532_CHIP_ID 0b1000010100100011
+unsigned short wm8523_get_chip_id(uint8_t portnum);
+
+/**
+ * Reset all register values to their defaults.
+ * Register: R0 [0:15] (write)
+ */
+void wm8523_reset_registers(uint8_t portnum);
+
+/**
+ * Read hardware revision counter.
+ * Register: R1 [0:2] (read)
+ */
+uint8_t wm8523_get_hardware_revision(uint8_t portnum);
+
+typedef enum __attribute__ ((packed)) {
+  WM8523_PWR_OFF                = 0b00,
+  WM8523_PWR_POWER_DOWN         = 0b01,
+  WM8523_PWR_POWER_UP_TO_MUTE   = 0b10,
+  WM8523_PWR_POWER_UP_TO_UNMUTE = 0b11,
+} wm8523_power_mode_t;
+
+/**
+ * Get current power mode.
+ * Register: R2 [0:1] (read)
+ */
+wm8523_power_mode_t wm8523_get_power_mode(uint8_t portnum);
+
+/**
+ * Set power mode.
+ * Register: R2 [0:1] (write)
+ */
+void wm8523_set_power_mode(uint8_t portnum, wm8523_power_mode_t mode);
+
+/*
+R3 [0:1]
+Audio Data Interface Format
+00 = Right justified
+01 = Left justified
+10 = I2S format
+11 = DSP mode
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_FMT_RIGHT = 0b00, // Right justified
+  WM8523_FMT_LEFT  = 0b01, // Left justified
+  WM8523_FMT_I2S   = 0b10, // I2S format
+  WM8523_FMT_DSP   = 0b11, // DSP mode
+} wm8523_audio_data_interface_format_t;
+
+/*
+R3 [3:4]
+Audio Data Word Length
+00 = 16 bits
+01 = 20 bits
+10 = 24 bits
+11 = 32 bits
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_WLEN_16 = 0b00, // 16 bit word length
+  WM8523_WLEN_20 = 0b01, // 20 bit word length
+  WM8523_WLEN_24 = 0b10, // 24 bit word length
+  WM8523_WLEN_32 = 0b11, // 32 bit word length
+} wm8523_audio_data_word_length_t;
+
+/*
+R3 [5]
+BCLK Inversion Control
+Slave mode:
+0 = Use rising edge
+1 = Use falling edge
+Master mode:
+0 = BCLK normal
+1 = BCLK inverted
+*/
+typedef enum __attribute__ ((packed)) {
+  // Slave mode:
+  WM8523_INVCTL_SLAVE_RISING    = 0b0, // Use rising edge
+  WM8523_INVCTL_SLAVE_FALLING   = 0b1, // Use falling edge
+
+  // Master mode:
+  WM8523_INVCTL_MASTER_NORMAL   = 0b0, // BCLK normal
+  WM8523_INVCTL_MASTER_INVERTED = 0b1, // BCLK inverted
+} wm8523_bclk_inversion_control_t;
+
+/*
+R3 [6]
+LRCLK Inversion Control
+0 = Normal polarity
+1 = Inverted polarity
+When AIF_FMT[2:0]=011 (DSP Mode):
+0 = Mode A (2nd clock)
+1 = Mode B (1st clock)
+*/
+typedef enum __attribute__ ((packed)) {
+  // Slave mode:
+  WM8523_LRCLKINVCTL_SLAVE_NORMAL   = 0b0, // Normal polarity
+  WM8523_LRCLKINVCTL_SLAVE_INVERTED = 0b1, // Inverted polarity
+
+  // Master mode:
+  WM8523_LRCLKINVCTL_MASTER_MODE_A  = 0b0, // Mode A (2nd clock)
+  WM8523_LRCLKINVCTL_MASTER_MODE_B  = 0b1, // Mode B (1st clock)
+} wm8523_lrclk_inversion_control_t;
+
+/*
+R3 [7]
+Master/Slave Select
+0 = Slave mode
+1 = Master mode
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_MODESEL_SLAVE  = 0b0, // Slave mode
+  WM8523_MODESEL_MASTER = 0b1, // Master mode
+} wm8523_slave_master_mode_sel_t;
+
+/*
+R3 [8]
+DAC De-emphasis Control
+0 = No de-emphasis
+1 = De-emphasis enabled
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_DEEMPH_DISABLED = 0b0, // No de-emphasis
+  WM8523_DEEMPH_ENABLED  = 0b1, // De-emphasis enabled
+} wm8523_dac_deemphasis_control_t;
+
+typedef struct __attribute__ ((packed)) {
+  wm8523_audio_data_interface_format_t fmt:2;
+  int reserved:1;
+  wm8523_audio_data_word_length_t wlen:2;
+  wm8523_bclk_inversion_control_t invctl:1;
+  wm8523_lrclk_inversion_control_t lrclkinvctl:1;
+  wm8523_slave_master_mode_sel_t modesel:1;
+  wm8523_dac_deemphasis_control_t deemp:1;
+  uint16_t blank:7;
+} wm8523_aif_ctrl1_t;
+
+void wm8523_set_aif_ctrl1(uint8_t portnum, wm8523_aif_ctrl1_t aif_ctrl1);
+wm8523_aif_ctrl1_t wm8523_get_aif_ctrl1(uint8_t portnum);
+
+/*
+R4 [0:2]
+MCLK:LRCLK Ratio
+000 = Auto detect
+001 = 128fs
+010 = 192fs
+011 = 256fs
+100 = 384fs
+101 = 512fs
+110 = 768fs
+111 = 1152fs
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_CLKRATIO_AUTO    = 0b000, // Auto detect
+  WM8523_CLKRATIO_128_FS  = 0b001, // 128fs
+  WM8523_CLKRATIO_192_FS  = 0b010, // 192fs
+  WM8523_CLKRATIO_256_FS  = 0b011, // 256fs
+  WM8523_CLKRATIO_384_FS  = 0b100, // 384fs
+  WM8523_CLKRATIO_512_FS  = 0b101, // 512fs
+  WM8523_CLKRATIO_768_FS  = 0b110, // 768fs
+  WM8523_CLKRATIO_1152_FS = 0b111, // 1152fs
+} wm8523_mclk_lrclk_ratio_t;
+
+/*
+R4 [3:5]
+BCLK Divider Control (Master Mode)
+000 = MCLK/4
+001 = MCLK/8
+010 = 32fs
+011 = 64fs
+100 = 128fs
+101 - 111 reserved
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_CLKDIV_MCLK_4 = 0b000, // MCLK/4
+  WM8523_CLKDIV_MCLK_8 = 0b001, // MCLK/8
+  WM8523_CLKDIV_32_FS  = 0b010, // 32fs
+  WM8523_CLKDIV_64_FS  = 0b011, // 64fs
+  WM8523_CLKDIV_128_FS = 0b100, // 128fs
+} wm8523_bclk_divider_control_t;
+
+/*
+R4 [6:7]
+Digital Monomix Control
+00 = Stereo (normal operation)
+01 = Mono (Left data to DACR)
+10 = Mono (Right data to DACL)
+11 = Digital monomix, (L+R)/2
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_MIX_STEREO     = 0b00, // Stereo (normal operation)
+  WM8523_MIX_MONO_LEFT  = 0b01, // Mono (Left data to DACR)
+  WM8523_MIX_MONO_RIGHT = 0b10, // Mono (Right data to DACL)
+  WM8523_MIX_MONO_MIXED = 0b11, // Digital monomix, (L+R)/2
+} wm8523_digital_monomix_control_t;
+
+typedef struct __attribute__ ((packed)) {
+  wm8523_mclk_lrclk_ratio_t clkratio:3;
+  wm8523_bclk_divider_control_t clkdiv:3;
+  wm8523_digital_monomix_control_t mix:2;
+} wm8523_aif_ctrl2_t;
+
+void wm8523_set_aif_ctrl2(uint8_t portnum, wm8523_aif_ctrl2_t aif_ctrl2);
+wm8523_aif_ctrl2_t wm8523_get_aif_ctrl2(uint8_t portnum);
+
+/*
+R5 [0]
+DAC Digital Volume Decrease Control
+0 = Apply volume decreases instantly (step)
+1 = Ramp volume decreases
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_VOL_DOWN_RAMP    = 0b00, // Apply volume decreases instantly (step)
+  WM8523_VOL_DOWN_INSTANT = 0b01, // Ramp volume decreases
+} wm8523_dac_vol_down_ramp_t;
+
+/*
+R5 [1]
+DAC Digital Volume Increase Control
+0 = Apply volume increases instantly (step)
+1 = Ramp volume increases
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_VOL_UP_RAMP    = 0b00, // Apply volume increases instantly (step)
+  WM8523_VOL_UP_INSTANT = 0b01, // Ramp volume increases
+} wm8523_dac_vol_up_ramp_t;
+
+/*
+R5 [2]
+Left DAC Mute
+0 = Normal operation
+1 = Mute
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_DACL_UNMUTE = 0b00, // Normal operation
+  WM8523_DACL_MUTE   = 0b01, // Mute
+} wm8523_dacl_mute_t;
+
+/*
+R5 [3]
+Right DAC Mute
+0 = Normal operation
+1 = Mute
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_DACR_UNMUTE = 0b00, // Normal operation
+  WM8523_DACR_MUTE   = 0b01, // Mute
+} wm8523_dacr_mute_t;
+
+/*
+R5 [4]
+Zero Cross Enable
+0 = Do not use zero cross
+1 = Use zero cross
+*/
+typedef enum __attribute__ ((packed)) {
+  WM8523_ZERO_CROSSING_DISABLED = 0b00, // Do not use zero cross
+  WM8523_ZERO_CROSSING_ENABLED  = 0b01, // Use zero cross
+} wm8523_dac_zc_t;
+
+typedef struct __attribute__ ((packed)) {
+  wm8523_dac_vol_down_ramp_t downramp:1;
+  wm8523_dac_vol_up_ramp_t upramp:1;
+  wm8523_dacl_mute_t lmute:1;
+  wm8523_dacr_mute_t rmute:1;
+  wm8523_dac_zc_t dac_zc:1;
+} wm8523_dac_ctrl3_t;
+
+void wm8523_set_dac_ctrl3(uint8_t portnum, wm8523_dac_ctrl3_t dac_ctrl3);
+wm8523_dac_ctrl3_t wm8523_get_dac_ctrl3(uint8_t portnum);
+
+/*
+R6 [0:8]
+Left DAC Digital Volume Control
+0 0000 0000 = -100dB
+0 0000 0001 = -99.75dB
+0 0000 0010 = -99.5dB
+...0.25dB steps
+1 1001 0000 = 0dB
+...0.25dB steps
+1 1011 1110 = +11.75dB
+1 11XX XXXX = +12dB
+*/
+
+/*
+R6 [9]
+Left DAC Digital Volume Update
+0 = Latch Left DAC volume setting into register map but do not update volume
+1 = Latch Left DAC volume setting into register map and update left and right channels simultaneously
+*/
+
+/*
+R7 [0:8]
+Right DAC Digital Volume Control
+0 0000 0000 = -100dB
+0 0000 0001 = -99.75dB
+0 0000 0010 = -99.5dB
+...0.25dB steps
+1 1001 0000 = 0dB
+...0.25dB steps
+1 1011 1110 = +11.75dB
+1 11XX XXXX = +12dB
+*/
+
+/*
+R7 [9]
+Right DAC Digital Volume Update
+0 = Latch Right DAC volume setting into register map but do not update volume
+1 = Latch Right DAC volume setting into register map and update left and right channels simultaneously
+*/
+
+/*
+R8 [0]
+Zero Detect Count Control
+0 = 1024
+1 = 2048
+*/
+
+
+void wm8523_tone();
+
+//void wm8523_configure();
+//uint16_t wm8523_read(uint8_t reg);
+//void wm8523_write(uint8_t reg, uint16_t data);
 
 #endif/*__PEDAL2METAL_WM8523_H__*/
-- 
cgit v1.2.3