From 6d9f9e37bf23dedb9d57b5589bbec74e28c515d0 Mon Sep 17 00:00:00 2001
From: Gregory Nutt <gnutt@nuttx.org>
Date: Sat, 19 Jul 2014 11:58:53 -0600
Subject: [PATCH] SAMA5D4-EK:  Add WM8904 initialization logic

---
 arch/arm/src/sama5/Kconfig          |   4 +-
 arch/arm/src/sama5/sam_ssc.c        |  24 ++-
 configs/sama5d4-ek/Kconfig          |  46 +---
 configs/sama5d4-ek/README.txt       | 165 ++++++---------
 configs/sama5d4-ek/src/Makefile     |   8 +
 configs/sama5d4-ek/src/sam_wm8904.c | 316 ++++++++++++++++++++++++++++
 configs/sama5d4-ek/src/sama5d4-ek.h |  61 ++++++
 drivers/audio/wm8904.c              |   2 +-
 include/nuttx/audio/wm8904.h        |  12 +-
 9 files changed, 485 insertions(+), 153 deletions(-)
 create mode 100644 configs/sama5d4-ek/src/sam_wm8904.c

diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig
index 7c0ced909b..3643a64a0c 100644
--- a/arch/arm/src/sama5/Kconfig
+++ b/arch/arm/src/sama5/Kconfig
@@ -483,14 +483,14 @@ config SAMA5_SSC0
 	bool "Synchronous Serial Controller 0 (SSC0)"
 	default n
 	select I2S
-	depends on SAMA5_DMAC0
+	depends on SAMA5_DMAC0 || SAMA5_XDMAC0 || SAMA5_XDMAC1
 	select AUDIO
 
 config SAMA5_SSC1
 	bool "Synchronous Serial Controller 1 (SSC1)"
 	default n
 	select I2S
-	depends on SAMA5_DMAC1
+	depends on SAMA5_DMAC1 || SAMA5_XDMAC0 || SAMA5_XDMAC1
 	select AUDIO
 
 config SAMA5_CAN0
diff --git a/arch/arm/src/sama5/sam_ssc.c b/arch/arm/src/sama5/sam_ssc.c
index 4750ecf18b..65cca17fd3 100644
--- a/arch/arm/src/sama5/sam_ssc.c
+++ b/arch/arm/src/sama5/sam_ssc.c
@@ -87,12 +87,28 @@
 #  define SAMA5_SSC_MAXINFLIGHT 16
 #endif
 
-#if defined(CONFIG_SAMA5_SSC0) && !defined(CONFIG_SAMA5_DMAC0)
-#  error CONFIG_SAMA5_DMAC0 required by SSC0
+#if defined(CONFIG_SAMA5_SSC0)
+#  if defined(CONFIG_SAMA5_HAVE_XDMA)
+#    if !defined(CONFIG_SAMA5_XDMAC0) && !defined(CONFIG_SAMA5_XDMAC1)
+#      error CONFIG_SAMA5_XDMAC1 (or XDMAC0) required by SSC0
+#    endif
+#  else
+#    if !defined(CONFIG_SAMA5_DMAC0)
+#      error CONFIG_SAMA5_DMAC0 required by SSC0
+#    endif
+#  endif
 #endif
 
-#if defined(CONFIG_SAMA5_SSC1) && !defined(CONFIG_SAMA5_DMAC1)
-#  error CONFIG_SAMA5_DMAC1 required by SSC1
+#if defined(CONFIG_SAMA5_SSC1)
+#  if defined(CONFIG_SAMA5_HAVE_XDMA)
+#    if !defined(CONFIG_SAMA5_XDMAC0) && !defined(CONFIG_SAMA5_XDMAC1)
+#      error CONFIG_SAMA5_XDMAC1 (or XDMAC0) required by SSC1
+#    endif
+#  else
+#    if !defined(CONFIG_SAMA5_DMAC1)
+#      error CONFIG_SAMA5_DMAC0 required by SSC1
+#    endif
+#  endif
 #endif
 
 #ifndef CONFIG_SAMA5_SSC0_DATALEN
diff --git a/configs/sama5d4-ek/Kconfig b/configs/sama5d4-ek/Kconfig
index dd25ecaafc..27d3112a40 100644
--- a/configs/sama5d4-ek/Kconfig
+++ b/configs/sama5d4-ek/Kconfig
@@ -198,43 +198,17 @@ config SAMA5D4EK_CHANNEL
 		Selects the PWM channel number that will be used to perform the PWM
 		test.  See apps/examples/pwm.
 
-if AUDIO_I2SCHAR && (SAMA5_SSC0 || SAMA5_SSC1)
+if AUDIO_WM8904
 
-if SAMA5_SSC0 && SAMA5_SSC1
-
-config SAMA5D4EK_SSC_PORT
-	int "SSC port number"
-	default 0
-	range 0 1
+config SAMA5D4EK_WM8904_I2CFREQUENCY
+	int "WM8904 I2C Frequency"
+	default 400000
+	range 1 400000
 	---help---
-		Specify the I2S port to use, i.e., 0 for SSC0 or 1 for SSC1
-
-endif # SAMA5_SSC0 && SAMA5_SSC1
-
-if SAMA5_SSC0 && !SAMA5_SSC1
-
-config SAMA5D4EK_SSC_PORT
-	int
-	default 0
-
-endif # SAMA5_SSC0 && !SAMA5_SSC1
-
-if !SAMA5_SSC0 && SAMA5_SSC1
-
-config SAMA5D4EK_SSC_PORT
-	int
-	default 1
-
-endif # SAMA5_SSC0 && !SAMA5_SSC1
-
-config SAMA5D4EK_I2SCHAR_MINOR
-	int "I2S character driver minor number"
-	default 0
-	---help---
-		The minor device number to use when registering the I2S character
-		device.  The driver will be registered at /dev/is2charN where N is
-		the value provided by this setting.
-
-endif # AUDIO_I2SCHAR && (SAMA5_SSC0 || SAMA5_SSC1)
+		This option selects the I2C frequency to use when communicating with
+		the WM8904 device.  The default, 400KHz, is the maximum supported by
+		the WM8904.  If you have problems communicating with the WM8904,
+		then you might want to try lowering this rate.
 
+endif # AUDIO_WM8904
 endif # ARCH_BOARD_SAMA5D4_EK
diff --git a/configs/sama5d4-ek/README.txt b/configs/sama5d4-ek/README.txt
index 9279ceaa64..91b961faf0 100644
--- a/configs/sama5d4-ek/README.txt
+++ b/configs/sama5d4-ek/README.txt
@@ -91,8 +91,7 @@ Contents
   - RTC
   - Watchdog Timer
   - TRNG and /dev/random
-  - I2S Audio Support
-  - WM8904 Support
+  - Audio Support
   - TM7000 LCD/Touchscreen
   - SAMA4D4-EK Configuration Options
   - Configurations
@@ -2709,116 +2708,84 @@ TRNG and /dev/random
       CONFIG_EXAMPLES_MAXSAMPLES=64       : Default settings are probably OK
       CONFIG_EXAMPLES_NSAMPLES=8
 
-I2S Audio Support
-=================
+Audio Support
+==============
 
+  WM8904 CODEC
+  ------------
   The SAMA4D4-EK has two devices on-board that can be used for verification
   of I2S functionality:  HDMI and a WM8904 audio CODEC.  As of this writing,
   the I2S driver is present, but there are not drivers for either the HDMI
   or the WM8904.
 
-  WM8904 Audio CODEC Interface
-  ----------------------------
+  WM8904 Audio CODEC Interface:
+  ---- ------------------ ---------------- ------------- ---------------------------------------
+  PIO  USAGE              BOARD SIGNAL     WM8904 PIN    NOTE
+  ---- ------------------ ---------------- ------------- ---------------------------------------
+  PA30 TWD0               AUDIO_TWD0_PA30  3  SDA        Pulled up, See J23 note below
+  PA31 TWCK0              AUDIO_TWCK0_PA31 2  SCLK       Pulled up
+  PB10 AUDIO_PCK2/EXP     AUDIO_PCK2_PB10  28 MCLK
+  PB27 AUDIO/HDMI_TK0/EXP AUDIO_TK0_PB27   29 BCLK/GPIO4 Note TK0 and RK0 are mutually exclusive
+  PB26 AUDIO_RK0          AUDIO_RK0_PB26   29 "  "/"   " "  " " " " " " " " " "      " "       "
+  PB30 AUDIO_RF/ZIG_TWCK2 AUDIO_RF0_PB30   30 LRCLK      Note TF0 and RF0 are mutually exclusive
+  PB31 AUDIO/HDMI_TF0/EXP AUDIO_TF0_PB31   30 "   "      "  " " " " " " " " " "      " "       "
+  PB29 AUDIO_RD0/ZIG_TWD2 AUDIO_RD0_PB29   31 ADCDAT
+  PB28 AUDIO/HDMI_TD0/EXP AUDIO_TD0_PB28   32 ACDAT
+  PE4  AUDIO_IRQ          AUDIO_IRQ_PE4    1  IRQ/GPIO1  Audio interrupt
+  ---- ------------------ ---------------- ------------- ---------------------------------------
+  Note that jumper J23 must be closed to connect AUDIO_TWD0_PA30
 
-    ------------- ---------------- -----------------
-    WM8904        SAMA5D4          NuttX Pin Name
-    ------------- ---------------- -----------------
-     3 SDA        PA30 TWD0        PIO_TWI0_D
-     2 SCLK       PA31 TWCK0       PIO_TWI0_CK
-    28 MCLK       PD30 PCK0        PIO_PMC_PCK0
-    29 BCLK/GPIO4 PC16 TK          PIO_SSC0_TK
-    "" "        " PC19 RK          PIO_SSC0_RK
-    30 LRCLK      PC17 TF          PIO_SSC0_TF
-    "" "   "      PC20 RF          PIO_SSC0_RF
-    31 ADCDAT     PC21 RD          PIO_SSC0_RD
-    32 DACDAT     PC18 TD          PIO_SSC0_TD
-     1 IRQ/GPIO1  PD16 INT_AUDIO   N/A
-    ------------- ---------------- -----------------
+  WM8904 Configuration
+  --------------------
+    System Type -> SAMA5 Peripheral Support
+      CONFIG_SAMA5_TWI0=y                   : Enable TWI0 driver support
+      CONFIG_SAMA5_SSCO=y                   : Enable SSC0 driver support
+      CONFIG_SAMA5_XDMAC1=y                 : XDMAC0 required by SSC0
+
+    System Type -> SSC0 Configuration
+      CONFIG_SAMA5_SSC_MAXINFLIGHT=16
+      CONFIG_SAMA5_SSC0_DATALEN=16
+
+    Device Drivers -> SPI Driver Support
+      CONFIG_SPI=y                          : Enable SPI support
+      CONFIG_SPI_EXCHANGE=y                 : Support the exchange method
+
+    System Type -> SSC Configuration
+      CONFIG_SAMA5_SSC_MAXINFLIGHT=16       : Up to 16 pending DMA transfers
+      CONFIG_SAMA5_SSC0_MASTER=y            : Master mode
+      CONFIG_SAMA5_SSC0_DATALEN=16          : 16-bit data
+      CONFIG_SAMA5_SSC0_RX=n                : No receiver
+      CONFIG_SAMA5_SSC0_TX=y                : Support a transmitter
+      CONFIG_SAMA5_SSC0_TX_MCKDIV=y         : Transmitter gets clock from MCK/2
+      CONFIG_SAMA5_SSC0_MCKDIV_SAMPLERATE=48000 : Sampling at 48K samples/sec
+      CONFIG_SAMA5_SSC0_TX_TKOUTPUT_XFR=y   : Outputs clock on TK when transferring data
+
+    Audio
+      CONFIG_AUDIO=y                        : Audio support needed
+      CONFIG_AUDIO_FORMAT_PCM=y             : Only PCM files are supported
+
+    Drivers -> Audio
+      CONFIG_I2S=y                          : General I2S support
+      CONFIG_AUDIO_DEVICES=y                : Audio device support
+      CONFIG_AUDIO_NUM_BUFFERS=8            : Number of audio buffers
+      CONFIG_AUDIO_BUFFER_NUMBYTES=8192     : Audio buffer size
+      CONFIG_AUDIO_WM8904=y                 : Build WM8904 driver character driver
+
+    Board Selection
+      CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY=400000
+
+    Library Routines
+      CONFIG_SCHED_WORKQUEUE=y              : MW8904 driver needs work queue support
 
   I2S Loopback Test
   -----------------
 
   The I2S driver was verified using a special I2C character driver (at
   nuttx/drivers/audio/i2schar.c) and a test driver at apps/examples/i2schar.
-  The I2S driver was verified in loopback mode with no audio device.
-
-  [NOTE: The above statement is anticipatory:  As of this writing I2S driver
-   verification is underway and still not complete].
-
-  This section describes the modifications to the NSH configuration that were
-  used to perform the I2S testing:
-
-    System Type -> SAMA5 Peripheral Support
-      CONFIG_SAMA5_SSCO=y              : Enable SSC0 driver support
-      CONFIG_SAMA5_DMAC0=y             : DMAC0 required by SSC0
-
-    Alternatively, SSC1 could have be used:
-
-    System Type -> SAMA5 Peripheral Support
-      CONFIG_SAMA5_SSC1=y              : Enable SSC0 driver support
-      CONFIG_SAMA5_DMAC1=y             : DMAC0 required by SSC0
-
-    System Type -> SSC Configuration
-      CONFIG_SAMA5_SSC_MAXINFLIGHT=16  : Up to 16 pending DMA transfers
-      CONFIG_SAMA5_SSC0_MASTER=y       : Master mode
-      CONFIG_SAMA5_SSC0_DATALEN=16     : 16-bit data
-      CONFIG_SAMA5_SSC0_RX=y           : Support a receiver
-      CONFIG_SAMA5_SSC0_RX_RKINPUT=y   : Receiver gets clock from RK input
-      CONFIG_SAMA5_SSC0_TX=y           : Support a transmitter
-      CONFIG_SAMA5_SSC0_TX_MCKDIV=y    : Transmitter gets clock from MCK/2
-      CONFIG_SAMA5_SSC0_MCKDIV_SAMPLERATE=48000 : Sampling at 48K samples/sec
-      CONFIG_SAMA5_SSC0_TX_TKOUTPUT_XFR=y  : Outputs clock on TK when transferring data
-      CONFIG_SAMA5_SSC0_LOOPBACK=y     : Loopmode mode connects RD/TD and RK/TK
-
-    Audio
-      CONFIG_AUDIO=y                   : Audio support needed
-                                       : Defaults should be okay
-
-    Drivers -> Audio
-      CONFIG_I2S=y                     : General I2S support
-      CONFIG_AUDIO_DEVICES=y           : Audio device support
-      CONFIG_AUDIO_I2SCHAR=y           : Build I2S character driver
-
-    The following describes how I have the test application at
-    apps/examples/i2schar configured:
-
-      CONFIG_EXAMPLES_I2SCHAR=y
-      CONFIG_EXAMPLES_I2SCHAR_DEVPATH="/dev/i2schar0"
-      CONFIG_EXAMPLES_I2SCHAR_TX=y
-      CONFIG_EXAMPLES_I2SCHAR_TXBUFFERS=4
-      CONFIG_EXAMPLES_I2SCHAR_TXSTACKSIZE=1536
-      CONFIG_EXAMPLES_I2SCHAR_RX=y
-      CONFIG_EXAMPLES_I2SCHAR_RXBUFFERS=4
-      CONFIG_EXAMPLES_I2SCHAR_RXSTACKSIZE=1536
-      CONFIG_EXAMPLES_I2SCHAR_BUFSIZE=256
-      CONFIG_EXAMPLES_I2SCHAR_DEVINIT=y
-
-    Board Selection
-      CONFIG_SAMA5D4EK_I2SCHAR_MINOR=0
-      CONFIG_SAMA5D4EK_SSC_PORT=0     : 0 or SSC0, 1 for SSC1
-
-    Library Routines
-      CONFIG_SCHED_WORKQUEUE=y          : Driver needs work queue support
-
-WM8904 Support
-==============
-
-  SAMA5D4 Interface
-  ---- ------------------ ---------------- ---------- ---------------------------------------
-  PIO  USAGE              BOARD SIGNAL     WM8904 PIN NOTE
-  ---- ------------------ ---------------- ---------- ---------------------------------------
-  PA30 TWD0               AUDIO_TWD0_PA30  SDA        Pulled up, See J23 note below
-  PA31 TWCK0              AUDIO_TWCK0_PA31 SCLK       Pulled up
-  PB10 AUDIO_PCK2/EXP     AUDIO_PCK2_PB10  MCLK
-  PB27 AUDIO/HDMI_TK0/EXP AUDIO_TK0_PB27   BCLK/GPIO4 Note TK0 and RK0 are mutually exclusive
-  PB26 AUDIO_RK0          AUDIO_RK0_PB26   "  "/"   " "  " " " " " " " " " "      " "       "
-  PB30 AUDIO_RF/ZIG_TWCK2 AUDIO_RF0_PB30   LRCLK      Note TF0 and RF0 are mutually exclusive
-  PB31 AUDIO/HDMI_TF0/EXP AUDIO_TF0_PB31   "   "      "  " " " " " " " " " "      " "       "
-  PB29 AUDIO_RD0/ZIG_TWD2 AUDIO_RD0_PB29   ADCDAT
-  PB28 AUDIO/HDMI_TD0/EXP AUDIO_TD0_PB28   ACDAT
-  PE4  AUDIO_IRQ          AUDIO_IRQ_PE4    IRQ/GPIO1  Audio interrupt
-  ---- ------------------ ---------------- ---------- ---------------------------------------
-  Note that jumper J23 must be closed to connect AUDIO_TWD0_PA30
+  The I2S driver was verified in loopback mode with no audio device.  That
+  test case has never been exercised on the SAMA454-EK.  See the README.txt
+  file at SAMA5D4-EK for information about how you might implement this test
+  for the SAMA5D4-EK.
 
 TM7000 LCD/Touchscreen
 ======================
diff --git a/configs/sama5d4-ek/src/Makefile b/configs/sama5d4-ek/src/Makefile
index 39779fea2a..0f29ff922c 100644
--- a/configs/sama5d4-ek/src/Makefile
+++ b/configs/sama5d4-ek/src/Makefile
@@ -76,6 +76,14 @@ CSRCS += sam_at25.c
 endif
 endif
 
+ifeq ($(CONFIG_AUDIO_WM8904),y)
+ifeq ($(CONFIG_SAMA5_TWI0),y)
+ifeq ($(CONFIG_SAMA5_SSC0),y)
+CSRCS += sam_wm8904.c
+endif
+endif
+endif
+
 ifeq ($(CONFIG_SAMA5_HSMCI0),y)
 CSRCS += sam_hsmci.c
 else
diff --git a/configs/sama5d4-ek/src/sam_wm8904.c b/configs/sama5d4-ek/src/sam_wm8904.c
new file mode 100644
index 0000000000..09d188b84c
--- /dev/null
+++ b/configs/sama5d4-ek/src/sam_wm8904.c
@@ -0,0 +1,316 @@
+/************************************************************************************
+ * configs/sama5d4-ek/src/sam_wm8904.c
+ *
+ *   Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ************************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <debug.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/i2c.h>
+#include <nuttx/audio/i2s.h>
+#include <nuttx/audio/wm8904.h>
+
+#include "up_arch.h"
+#include "sam_pio.h"
+#include "sam_twi.h"
+#include "sam_ssc.h"
+
+#include "sama5d4-ek.h"
+
+#ifdef HAVE_WM8904
+
+/****************************************************************************
+ * Pre-Processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct sama5d4ek_mwinfo_s
+{
+  /* Standard MW8904 interface */
+
+  struct wm8904_lower_s lower;
+
+  /* Extensions for the sama5d4ek board */
+
+  wm8904_handler_t handler;
+  FAR void *arg;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* IRQ/PIO access callbacks.  These operations all hidden behind
+ * callbacks to isolate the WM8904 driver from differences in PIO
+ * interrupt handling by varying boards and MCUs.  If possible,
+ * interrupts should be configured on both rising and falling edges
+ * so that contact and loss-of-contact events can be detected.
+ *
+ *   attach  - Attach the WM8904 interrupt handler to the PIO interrupt
+ *   enable  - Enable or disable the PIO interrupt
+ */
+
+static int  wm8904_attach(FAR const struct wm8904_lower_s *lower,
+                          wm8904_handler_t isr, FAR void *arg);
+static void wm8904_enable(FAR const struct wm8904_lower_s *lower,
+                          bool enable);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* A reference to a structure of this type must be passed to the WM8904
+ * driver.  This structure provides information about the configuration
+ * of the WM8904 and provides some board-specific hooks.
+ *
+ * Memory for this structure is provided by the caller.  It is not copied
+ * by the driver and is presumed to persist while the driver is active.
+ */
+
+static struct sama5d4ek_mwinfo_s g_mxtinfo =
+{
+  .lower =
+  {
+    .address   = WM8904_I2C_ADDRESS,
+    .frequency = CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY,
+
+    .attach    = wm8904_attach,
+    .enable    = wm8904_enable,
+  },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * IRQ/PIO access callbacks.  These operations all hidden behind
+ * callbacks to isolate the WM8904 driver from differences in PIO
+ * interrupt handling by varying boards and MCUs.  If possible,
+ * interrupts should be configured on both rising and falling edges
+ * so that contact and loss-of-contact events can be detected.
+ *
+ *   attach  - Attach the WM8904 interrupt handler to the PIO interrupt
+ *   enable  - Enable or disable the PIO interrupt
+ *   clear   - Acknowledge/clear any pending PIO interrupt
+ *
+ ****************************************************************************/
+
+static int wm8904_attach(FAR const struct wm8904_lower_s *lower,
+                         wm8904_handler_t isr,  FAR void *arg)
+{
+  if (isr)
+    {
+      /* Just save the address of the handler and its argument for now.  The
+       * new handler will called via wm8904_interrupt() when the interrupt occurs.
+       */
+
+      ivdbg("Attaching %p\n", isr);
+      g_mxtinfo.handler = isr;
+      g_mxtinfo.arg = arg;
+    }
+  else
+    {
+      ivdbg("Detaching %p\n", g_mxtinfo.handler);
+      wm8904_enable(lower, false);
+      g_mxtinfo.handler = NULL;
+      g_mxtinfo.arg = NULL;
+    }
+
+  return OK;
+}
+
+static void wm8904_enable(FAR const struct wm8904_lower_s *lower, bool enable)
+{
+  /* Enable or disable interrupts */
+
+  if (enable && g_mxtinfo.handler)
+    {
+      sam_pioirqenable(IRQ_INT_WM8904);
+    }
+  else
+    {
+      sam_pioirqdisable(IRQ_INT_WM8904);
+    }
+}
+
+static int wm8904_interrupt(int irq, FAR void *context)
+{
+  /* Just forward the interrupt to the WM8904 driver */
+
+  if (g_mxtinfo.handler)
+    {
+      return g_mxtinfo.handler(&g_mxtinfo.lower, g_mxtinfo.arg);
+    }
+
+  /* We got an interrupt with no handler.  This should not
+   * happen.
+   */
+
+  sam_pioirqdisable(IRQ_INT_WM8904);
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: sam_wm8904_initialize
+ *
+ * Description:
+ *   This function is called by platform-specific, setup logic to configure
+ *   and register the WM8904 device.  This function will register the driver
+ *   as /dev/wm8904[x] where x is determined by the minor device number.
+ *
+ * Input Parameters:
+ *   minor - The input device minor number
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+int sam_wm8904_initialize(int minor)
+{
+  FAR struct audio_lowerhalf_s *audio;
+  FAR struct i2c_dev_s *i2c;
+  FAR struct i2s_dev_s *i2s;
+  static bool initialized = false;
+  char devname[8];
+  int ret;
+
+  auddbg("minor %d\n", minor);
+  DEBUGASSERT(minor >= 0 && minor <= 25);
+
+  /* Have we already initialized?  Since we never uninitialize we must prevent
+   * multiple initializations.  This is necessary, for example, when the
+   * touchscreen example is used as a built-in application in NSH and can be
+   * called numerous time.  It will attempt to initialize each time.
+   */
+
+  if (!initialized)
+    {
+      /* Configure the WM8904 interrupt pin */
+
+      (void)sam_configpio(PIO_INT_WM8904);
+
+      /* Get an instance of the I2C interface for the WM8904 chip select */
+
+      i2c = up_i2cinitialize(WM8904_TWI_BUS);
+      if (!i2c)
+        {
+          auddbg("Failed to initialize TWI%d\n", WM8904_TWI_BUS);
+          ret = -ENODEV;
+          goto errout;
+        }
+
+      /* Get an instance of the I2S interface for the WM8904 data channel */
+
+      i2s = sam_ssc_initialize(WM8904_SSC_BUS);
+      if (!i2s)
+        {
+          auddbg("Failed to initialize SSC%d\n", WM8904_SSC_BUS);
+          ret = -ENODEV;
+          goto errout_with_i2c;
+        }
+
+      /* Now we can use these I2C and I2S interfaces to initialize the
+       * MW8904 which will return an audio interface.
+       */
+
+      audio = wm8904_initialize(i2c, i2s, &g_mxtinfo.lower, minor);
+      if (!audio)
+        {
+          auddbg("Failed to initialize the WM8904\n");
+          ret = -ENODEV;
+          goto errout_with_i2s;
+        }
+
+      /* Configure WM8904 interrupts */
+
+      sam_pioirq(PIO_INT_WM8904);
+      ret = irq_attach(IRQ_INT_WM8904, wm8904_interrupt);
+      if (ret < 0)
+        {
+          auddbg("ERROR: Failed to register WM8904 device: %d\n", ret);
+          goto errout_with_audio;
+        }
+
+      /* Create a device name */
+
+      snprintf(devname, 8, "wm8904%c", 'a' + minor);
+
+      /* Register the WM8904 audio device */
+
+      ret = audio_register(devname, audio);
+      if (ret < 0)
+        {
+          auddbg("ERROR: Failed to register /dev/%s device: %d\n", devname, ret);
+          goto errout_with_irq;
+        }
+
+      /* Now we are initialized */
+
+      initialized = true;
+    }
+
+  return OK;
+
+  /* Error exits.  Unfortunately there is no mechanism in place now to
+   * recover errors on initialization failures.
+   */
+
+errout_with_irq:
+  irq_detach(IRQ_INT_WM8904);
+errout_with_audio:
+errout_with_i2s:
+errout_with_i2c:
+errout:
+  return ret;
+}
+
+#endif /* HAVE_WM8904 */
diff --git a/configs/sama5d4-ek/src/sama5d4-ek.h b/configs/sama5d4-ek/src/sama5d4-ek.h
index c539202af9..5286716b5a 100644
--- a/configs/sama5d4-ek/src/sama5d4-ek.h
+++ b/configs/sama5d4-ek/src/sama5d4-ek.h
@@ -64,6 +64,7 @@
 #define HAVE_USBMONITOR 1
 #define HAVE_NETWORK    1
 #define HAVE_MAXTOUCH   1
+#define HAVE_WM8904     1
 
 /* HSMCI */
 /* Can't support MMC/SD if the card interface(s) are not enable */
@@ -315,6 +316,36 @@
 #  endif
 #endif
 
+/* Audio */
+/* Default configuration values */
+
+#ifndef CONFIG_AUDIO_WM8904
+#  undef HAVE_WM8904
+#endif
+
+#ifdef HAVE_WM8904
+#  ifndef CONFIG_SAMA5_TWI0
+#    warning CONFIG_SAMA5_TWI0 is required for audio support
+#    undef HAVE_WM8904
+#  endif
+
+#  ifndef CONFIG_SAMA5_SSC0
+#    warning CONFIG_SAMA5_SSC0 is required for audio support
+#    undef HAVE_WM8904
+#  endif
+
+#  ifndef CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY
+#    warning Defaulting to maximum WM8904 I2C frequency
+#    define CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY 400000
+#  endif
+
+#  if CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY > 400000
+#    warning WM8904 I2C frequency cannot exceed 400KHz
+#    undef CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY
+#    define CONFIG_SAMA5D4EK_WM8904_I2CFREQUENCY 400000
+#  endif
+#endif
+
 /* LEDs *****************************************************************************/
 /* There are 3 LEDs on the SAMA5D4-EK:
  *
@@ -649,6 +680,15 @@
                         PIO_INT_BOTHEDGES | PIO_PORT_PIOE | PIO_PIN4)
 #define IRQ_INT_WM8904 SAM_IRQ_PE4
 
+/* The MW8904 communicates on TWI0, I2C address 0x1a for control operations */
+
+#define WM8904_TWI_BUS      0
+#define WM8904_I2C_ADDRESS  0x1a
+
+/* The MW8904 transfers data on SSC0 */
+
+#define WM8904_SSC_BUS      0
+
 /* SPI Chip Selects *****************************************************************/
 /* The SAMA5D4-EK includes an Atmel AT25DF321A, 32-megabit, 2.7-volt SPI serial
  * FLASH on board.  The connection is as follows:
@@ -859,6 +899,27 @@ void board_led_initialize(void);
 int nsh_archinitialize(void);
 #endif
 
+/****************************************************************************
+ * Name: sam_wm8904_initialize
+ *
+ * Description:
+ *   This function is called by platform-specific, setup logic to configure
+ *   and register the WM8904 device.  This function will register the driver
+ *   as /dev/wm8904[x] where x is determined by the minor device number.
+ *
+ * Input Parameters:
+ *   minor - The input device minor number
+ *
+ * Returned Value:
+ *   Zero is returned on success.  Otherwise, a negated errno value is
+ *   returned to indicate the nature of the failure.
+ *
+ ****************************************************************************/
+
+#ifdef HAVE_WM8904
+int sam_wm8904_initialize(int minor);
+#endif /* HAVE_WM8904 */
+
 #endif /* __ASSEMBLY__ */
 #endif /* __CONFIGS_SAMA5D4_EK_SRC_SAMA5D4_EK_H */
 
diff --git a/drivers/audio/wm8904.c b/drivers/audio/wm8904.c
index 8029316cde..e9d92bbd64 100644
--- a/drivers/audio/wm8904.c
+++ b/drivers/audio/wm8904.c
@@ -1843,7 +1843,7 @@ FAR struct audio_lowerhalf_s *
           auddbg("ERROR: WM8904 not found: ID=%04x\n", regval);
           return -ENODEV;
         }
-WM8904_DUMMY
+
       /* Configure the WM8904 hardware as an audio input device */
 
       wm8904_audio_output(priv);
diff --git a/include/nuttx/audio/wm8904.h b/include/nuttx/audio/wm8904.h
index e9e03a04a9..4b84f6d2a9 100644
--- a/include/nuttx/audio/wm8904.h
+++ b/include/nuttx/audio/wm8904.h
@@ -59,7 +59,7 @@
  * CONFIG_AUDIO_WM8904 - Enabled WM8904 support
  */
 
-/* Pre-requisistes */
+/* Pre-requisites */
 
 #ifndef CONFIG_AUDIO
 #  error CONFIG_AUDIO is required for audio subsystem support
@@ -83,16 +83,6 @@
 
 /* Default configuration values */
 
-#ifndef CONFIG_WM8904_I2CFREQUENCY
-#  define CONFIG_WM8904_I2CFREQUENCY 400000
-#endif
-
-#if CONFIG_WM8904_I2CFREQUENCY > 400000
-#  warning WM8904 I2C frequency cannot exceed 400KHz
-#  undef CONFIG_WM8904_I2CFREQUENCY
-#  define CONFIG_WM8904_I2CFREQUENCY 400000
-#endif
-
 /* Helper macros ************************************************************/
 
 #define WM8904_ATTACH(s,isr,arg) ((s)->attach(s,isr,arg))