You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2022/11/08 13:03:24 UTC

[incubator-nuttx] 01/02: audio: Add support for the ES8388 codec (output)

This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit fa0ccce046dbb5f4db64851a600c55b432445f28
Author: Lucas Saavedra Vaz <lu...@espressif.com>
AuthorDate: Mon Nov 7 14:07:16 2022 -0300

    audio: Add support for the ES8388 codec (output)
---
 drivers/audio/Kconfig        |   50 +
 drivers/audio/Make.defs      |    7 +
 drivers/audio/es8388.c       | 2428 ++++++++++++++++++++++++++++++++++++++++++
 drivers/audio/es8388.h       | 1179 ++++++++++++++++++++
 drivers/audio/es8388_debug.c |  163 +++
 include/nuttx/audio/audio.h  |   25 +
 include/nuttx/audio/es8388.h |  191 ++++
 7 files changed, 4043 insertions(+)

diff --git a/drivers/audio/Kconfig b/drivers/audio/Kconfig
index a7f388c250..0a4a689619 100644
--- a/drivers/audio/Kconfig
+++ b/drivers/audio/Kconfig
@@ -206,6 +206,56 @@ config CS4344_WORKER_STACKSIZE
 
 endif # AUDIO_CS4344
 
+config AUDIO_ES8388
+	bool "ES8388 codec chip"
+	default n
+	depends on AUDIO
+	---help---
+		Select to enable support for the ES8388 Audio codec by Everest
+		Semiconductor.
+
+		NOTE: This driver also depends on both I2C and I2S support although
+		that dependency is not explicit here.
+
+if AUDIO_ES8388
+
+config ES8388_INPUT_INITVOLUME
+	int "ES8388 initial input volume setting"
+	default 1000
+
+config ES8388_OUTPUT_INITVOLUME
+	int "ES8388 initial output volume setting"
+	default 400
+
+config ES8388_INFLIGHT
+	int "ES8388 maximum in-flight audio buffers"
+	default 2
+
+config ES8388_MSG_PRIO
+	int "ES8388 message priority"
+	default 1
+
+config ES8388_BUFFER_SIZE
+	int "ES8388 preferred buffer size"
+	default 8192
+
+config ES8388_NUM_BUFFERS
+	int "ES8388 preferred number of buffers"
+	default 4
+
+config ES8388_WORKER_STACKSIZE
+	int "ES8388 worker thread stack size"
+	default 2048
+
+config ES8388_REGDUMP
+	bool "ES8388 register dump"
+	default n
+	depends on DEBUG_FEATURES
+	---help---
+		Enable logic to dump the contents of all ES8388 registers.
+
+endif # AUDIO_ES8388
+
 config AUDIO_WM8776
 	bool "WM8776 audio chip"
 	default n
diff --git a/drivers/audio/Make.defs b/drivers/audio/Make.defs
index 144a0f90a2..33109cba24 100644
--- a/drivers/audio/Make.defs
+++ b/drivers/audio/Make.defs
@@ -48,6 +48,13 @@ ifeq ($(CONFIG_AUDIO_CS4344),y)
 CSRCS += cs4344.c
 endif
 
+ifeq ($(CONFIG_AUDIO_ES8388),y)
+CSRCS += es8388.c
+ifeq ($(CONFIG_ES8388_REGDUMP),y)
+CSRCS += es8388_debug.c
+endif
+endif
+
 ifeq ($(CONFIG_AUDIO_WM8994),y)
 CSRCS += wm8994.c
 ifeq ($(CONFIG_WM8994_REGDUMP),y)
diff --git a/drivers/audio/es8388.c b/drivers/audio/es8388.c
new file mode 100644
index 0000000000..0a59e03628
--- /dev/null
+++ b/drivers/audio/es8388.c
@@ -0,0 +1,2428 @@
+/****************************************************************************
+ * drivers/audio/es8388.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+#include <fixedmath.h>
+#include <debug.h>
+#include <pthread.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/mqueue.h>
+#include <nuttx/queue.h>
+#include <nuttx/clock.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/fs/ioctl.h>
+#include <nuttx/audio/i2s.h>
+#include <nuttx/audio/audio.h>
+#include <nuttx/audio/es8388.h>
+
+#include "es8388.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef ARRAY_SIZE
+#  define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+#if !defined(CONFIG_ES8388_REGDUMP)
+static
+#endif
+uint8_t     es8388_readreg(FAR struct es8388_dev_s *priv, uint8_t regaddr);
+static void es8388_writereg(FAR struct es8388_dev_s *priv,
+                            uint8_t regaddr,
+                            uint16_t regval);
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+static void es8388_setvolume(FAR struct es8388_dev_s *priv,
+                             uint16_t volume);
+#endif
+static void es8388_setmclkfrequency(FAR struct es8388_dev_s *priv);
+static void es8388_setmute(FAR struct es8388_dev_s *priv, bool enable);
+static void es8388_setbitspersample(FAR struct es8388_dev_s *priv);
+static void es8388_setsamplerate(FAR struct es8388_dev_s *priv);
+static int  es8388_getcaps(FAR struct audio_lowerhalf_s *dev,
+                           int type,
+                           FAR struct audio_caps_s *caps);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int  es8388_configure(FAR struct audio_lowerhalf_s *dev,
+                             FAR void *session,
+                             FAR const struct audio_caps_s *caps);
+#else
+static int  es8388_configure(FAR struct audio_lowerhalf_s *dev,
+                             FAR const struct audio_caps_s *caps);
+#endif
+static int  es8388_shutdown(FAR struct audio_lowerhalf_s *dev);
+static void es8388_senddone(FAR struct i2s_dev_s *i2s,
+                            FAR struct ap_buffer_s *apb,
+                            FAR void *arg,
+                            int result);
+static void es8388_returnbuffers(FAR struct es8388_dev_s *priv);
+static int  es8388_sendbuffer(FAR struct es8388_dev_s *priv);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int  es8388_start(FAR struct audio_lowerhalf_s *dev,
+                         FAR void *session);
+#else
+static int  es8388_start(FAR struct audio_lowerhalf_s *dev);
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int  es8388_stop(FAR struct audio_lowerhalf_s *dev,
+                        FAR void *session);
+#else
+static int  es8388_stop(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int  es8388_pause(FAR struct audio_lowerhalf_s *dev,
+                         FAR void *session);
+static int  es8388_resume(FAR struct audio_lowerhalf_s *dev,
+                          FAR void *session);
+#else
+static int  es8388_pause(FAR struct audio_lowerhalf_s *dev);
+static int  es8388_resume(FAR struct audio_lowerhalf_s *dev);
+#endif
+#endif
+static int  es8388_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
+                                 FAR struct ap_buffer_s *apb);
+static int  es8388_cancelbuffer(FAR struct audio_lowerhalf_s *dev,
+                                FAR struct ap_buffer_s *apb);
+static int  es8388_ioctl(FAR struct audio_lowerhalf_s *dev,
+                         int cmd,
+                         unsigned long arg);
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int  es8388_reserve(FAR struct audio_lowerhalf_s *dev,
+                          FAR void **session);
+#else
+static int  es8388_reserve(FAR struct audio_lowerhalf_s *dev);
+#endif
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int  es8388_release(FAR struct audio_lowerhalf_s *dev,
+                           FAR void *session);
+#else
+static int  es8388_release(FAR struct audio_lowerhalf_s *dev);
+#endif
+static void *es8388_workerthread(pthread_addr_t pvarg);
+static void es8388_audio_output(FAR struct es8388_dev_s *priv);
+#if 0
+static void es8388_audio_input(FAR struct es8388_dev_s *priv);
+#endif
+static void es8388_reset(FAR struct es8388_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct audio_ops_s g_audioops =
+{
+  es8388_getcaps,       /* getcaps        */
+  es8388_configure,     /* configure      */
+  es8388_shutdown,      /* shutdown       */
+  es8388_start,         /* start          */
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+  es8388_stop,          /* stop           */
+#endif
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+  es8388_pause,         /* pause          */
+  es8388_resume,        /* resume         */
+#endif
+  NULL,                 /* allocbuffer    */
+  NULL,                 /* freebuffer     */
+  es8388_enqueuebuffer, /* enqueue_buffer */
+  es8388_cancelbuffer,  /* cancel_buffer  */
+  es8388_ioctl,         /* ioctl          */
+  NULL,                 /* read           */
+  NULL,                 /* write          */
+  es8388_reserve,       /* reserve        */
+  es8388_release        /* release        */
+};
+
+/****************************************************************************
+ * Name: es8388_readreg
+ *
+ * Description:
+ *    Read the specified 8-bit register from the ES8388 device through I2C.
+ *
+ *   priv - A reference to the driver state structure.
+ *   regaddr - Address of the register to be read.
+ *
+ * Returned Value:
+ *   On success, the byte stored in the register.
+ *   On failure, 0.
+ *
+ ****************************************************************************/
+
+#if !defined(CONFIG_ES8388_REGDUMP)
+static
+#endif
+uint8_t es8388_readreg(FAR struct es8388_dev_s *priv, uint8_t regaddr)
+{
+  int retries;
+
+  /* Try up to three times to read the register */
+
+  for (retries = 0; retries < 3; retries++)
+    {
+      struct i2c_msg_s msg[2];
+      uint8_t data;
+      int ret;
+
+      /* Set up to write the address */
+
+      msg[0].frequency = priv->lower->frequency;
+      msg[0].addr      = priv->lower->address;
+      msg[0].flags     = 0;
+      msg[0].buffer    = &regaddr;
+      msg[0].length    = 1;
+
+      /* Followed by the read data */
+
+      msg[1].frequency = priv->lower->frequency;
+      msg[1].addr      = priv->lower->address;
+      msg[1].flags     = I2C_M_READ;
+      msg[1].buffer    = &data;
+      msg[1].length    = 12;
+
+      /* Read the register data. The returned value is the number messages
+       * completed.
+       */
+
+      ret = I2C_TRANSFER(priv->i2c, msg, 2);
+      if (ret < 0)
+        {
+#ifdef CONFIG_I2C_RESET
+          /* Perhaps the I2C bus is locked up? Try to shake the bus free */
+
+          audwarn("WARNING: I2C_TRANSFER failed: %d ... Resetting\n", ret);
+
+          ret = I2C_RESET(priv->i2c);
+          if (ret < 0)
+            {
+              auderr("ERROR: I2C_RESET failed: %d\n", ret);
+              break;
+            }
+#else
+          auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+#endif
+        }
+      else
+        {
+          /* The I2C transfer was successful... break out of the loop and
+           * return the value read.
+           */
+
+          audinfo("Read: %02x -> %02x\n", regaddr, data);
+          return data;
+        }
+
+      audinfo("retries=%d regaddr=%02x\n", retries, regaddr);
+    }
+
+  /* No error indication is returned on a failure... just return zero */
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: es8388_writereg
+ *
+ * Description:
+ *   Write the specified 8-bit register to the ES8388 device through I2C.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *   regaddr - Address of the register to be written.
+ *   regval - Value to be written.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void es8388_writereg(FAR struct es8388_dev_s *priv,
+                            uint8_t regaddr,
+                            uint16_t regval)
+{
+  struct i2c_config_s config;
+  int retries;
+
+  /* Setup up the I2C configuration */
+
+  config.frequency = priv->lower->frequency;
+  config.address   = priv->lower->address;
+  config.addrlen   = 7;
+
+  /* Try up to three times to write the register */
+
+  for (retries = 0; retries < 3; retries++)
+    {
+      uint8_t data[2];
+      int ret;
+
+      /* Set up the data to write */
+
+      data[0] = regaddr;
+      data[1] = regval;
+
+      /* Read the register data. The returned value is the number messages
+       * completed.
+       */
+
+      ret = i2c_write(priv->i2c, &config, data, 2);
+      if (ret < 0)
+        {
+#ifdef CONFIG_I2C_RESET
+          /* Perhaps the I2C bus is locked up? Try to shake the bus free */
+
+          audwarn("WARNING: i2c_write failed: %d ... Resetting\n", ret);
+
+          ret = I2C_RESET(priv->i2c);
+          if (ret < 0)
+            {
+              auderr("ERROR: I2C_RESET failed: %d\n", ret);
+              break;
+            }
+#else
+          auderr("ERROR: I2C_TRANSFER failed: %d\n", ret);
+#endif
+        }
+      else
+        {
+          /* The I2C transfer was successful... break out of the loop and
+           * return.
+           */
+
+          audinfo("Write: %02x <- %02x\n", regaddr, regval);
+          return;
+        }
+
+      audinfo("retries=%d regaddr=%02x\n", retries, regaddr);
+    }
+}
+
+/****************************************************************************
+ * Name: es8388_setvolume
+ *
+ * Description:
+ *   Set the right and left volume values in the ES8388 device based on the
+ *   desired volume and balance settings.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *   volume - The volume to be set in the codec (0..1000).
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+static void es8388_setvolume(FAR struct es8388_dev_s *priv, uint16_t volume)
+{
+  uint16_t leftlvl;
+  int16_t dbleftlvl;
+  uint16_t rightlvl;
+  int16_t dbrightlvl;
+
+  if (volume > AUDIO_VOLUME_MAX)
+    {
+      audwarn("Warning: Volume > AUDIO_VOLUME_MAX!\n");
+      volume = AUDIO_VOLUME_MAX;
+    }
+
+  audinfo("Volume = %u\n", volume);
+
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+  /* Calculate the left channel volume level {0..1000} */
+
+  if (priv->balance <= AUDIO_BALANCE_CENTER)
+    {
+      leftlvl = volume;
+    }
+  else if (priv->balance == AUDIO_BALANCE_RIGHT)
+    {
+      leftlvl = AUDIO_VOLUME_MIN;
+    }
+  else
+    {
+      leftlvl = ((((AUDIO_BALANCE_RIGHT - priv->balance) * 100) /
+        AUDIO_BALANCE_CENTER) * volume) / 100;
+    }
+
+  /* Calculate the right channel volume level {0..1000} */
+
+  if (priv->balance >= AUDIO_BALANCE_CENTER)
+    {
+      rightlvl = volume;
+    }
+  else if (priv->balance == AUDIO_BALANCE_LEFT)
+    {
+      rightlvl = AUDIO_VOLUME_MIN;
+    }
+  else
+    {
+      rightlvl = (((priv->balance * 100) / AUDIO_BALANCE_CENTER) * volume) /
+        100;
+    }
+
+#  else
+  leftlvl = priv->volume;
+  rightlvl = priv->volume;
+#  endif
+
+  /* Convert from (0..1000) to (-96..0) */
+
+  dbleftlvl = (int16_t)
+    (leftlvl ? (20 * log10f(rightlvl / AUDIO_VOLUME_MAX_FLOAT)) : -96);
+  dbrightlvl = (int16_t)
+    (rightlvl ? (20 * log10f(rightlvl / AUDIO_VOLUME_MAX_FLOAT)) : -96);
+
+  audinfo("Volume: dbleftlvl = %d, dbrightlvl = %d\n",
+          dbleftlvl, dbrightlvl);
+
+  /* Convert and truncate to 1 byte */
+
+  dbleftlvl = ((-dbleftlvl) << 1) & 0xff;
+  dbrightlvl = ((-dbrightlvl) << 1) & 0xff;
+
+  /* Set the volume */
+
+  if (priv->audio_mode == ES8388_MODULE_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL4, ES8388_LDACVOL(dbleftlvl));
+      es8388_writereg(priv, ES8388_DACCONTROL5, ES8388_RDACVOL(dbrightlvl));
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_ADC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_ADCCONTROL8, ES8388_LADCVOL(dbleftlvl));
+      es8388_writereg(priv, ES8388_ADCCONTROL9, ES8388_RADCVOL(dbrightlvl));
+    }
+
+  /* Remember the volume level and mute settings */
+
+  priv->volume = volume;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
+
+/****************************************************************************
+ * Name: es8388_setmclkfrequency
+ *
+ * Description:
+ *   Set the frequency of the I2S' Master Clock (MCLK).
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static void es8388_setmclkfrequency(FAR struct es8388_dev_s *priv)
+{
+  priv->mclk = 0;
+
+  for (int i = 0; i < ARRAY_SIZE(es8388_mclk_rate); i++)
+    {
+      if (es8388_mclk_rate[i].sample_rate == priv->samprate)
+        {
+          /* Normally master clock should be multiple of the sample rate
+           * and bclk at the same time. The field mclk_rate_s::multiple
+           * means the multiple of mclk to the sample rate. If data width
+           * is 24 bits, in order to keep mclk a multiple to the bclk,
+           * mclk_rate_s::multiple should be a divisible by 3, otherwise
+           * the ws signal will be inaccurate.
+           */
+
+          priv->mclk = es8388_mclk_rate[i].mclk;
+
+          if (es8388_mclk_rate[i].multiple % (priv->bpsamp / 8) == 0)
+            {
+              break;
+            }
+        }
+    }
+
+  if (priv->mclk)
+    {
+      audinfo("MCLK Freq: %u\n", priv->mclk);
+
+      int ret = I2S_MCLKFREQUENCY(priv->i2s, priv->mclk);
+
+      if (ret < 0)
+        {
+          if (ret != -ENOTTY)
+            {
+              auderr("ERROR: Failed to set the MCLK on lower half\n");
+            }
+          else
+            {
+              priv->mclk = 0;
+              auderr("WARNING: MCLK cannot be set on lower half\n");
+            }
+        }
+    }
+  else
+    {
+      auderr("ERROR: Unsupported combination of sample rate and"
+                       " data width\n");
+    }
+}
+
+/****************************************************************************
+ * Name: es8388_setmute
+ *
+ * Description:
+ *   Mute/unmute the ADC or DAC of the codec based on the current settings.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *   enable - Boolean to enable or disable the mute function.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void es8388_setmute(FAR struct es8388_dev_s *priv, bool enable)
+{
+  uint8_t reg = 0;
+
+  audinfo("Volume: mute=%d\n", (int)enable);
+
+  priv->mute = enable;
+
+  if (priv->audio_mode == ES8388_MODULE_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      reg = es8388_readreg(priv, ES8388_DACCONTROL3) &
+        (~ES8388_DACMUTE_BITMASK);
+      es8388_writereg(priv, ES8388_DACCONTROL3,
+                      reg | ES8388_DACMUTE(enable));
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_ADC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      reg = es8388_readreg(priv, ES8388_ADCCONTROL7) &
+        (~ES8388_ADCMUTE_BITMASK);
+      es8388_writereg(priv, ES8388_ADCCONTROL7,
+                      reg | ES8388_ADCMUTE(enable));
+    }
+}
+
+/****************************************************************************
+ * Name: es8388_setbitspersample
+ *
+ * Description:
+ *   Set the number of bits per sample used by the I2S driver and the codec.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void es8388_setbitspersample(FAR struct es8388_dev_s *priv)
+{
+  uint8_t reg;
+  uint8_t bit_config;
+
+  DEBUGASSERT(priv && priv->lower);
+
+  switch (priv->bpsamp)
+    {
+      case 16:
+        bit_config = ES8388_WORD_LENGTH_16BITS;
+        break;
+
+      case 18:
+        bit_config = ES8388_WORD_LENGTH_18BITS;
+        break;
+
+      case 20:
+        bit_config = ES8388_WORD_LENGTH_20BITS;
+        break;
+
+      case 24:
+        bit_config = ES8388_WORD_LENGTH_24BITS;
+        break;
+
+      case 32:
+        bit_config = ES8388_WORD_LENGTH_32BITS;
+        break;
+
+      default:
+        audwarn("ERROR: Data length not supported.\n");
+        return;
+    }
+
+  I2S_TXDATAWIDTH(priv->i2s, priv->bpsamp);
+  I2S_RXDATAWIDTH(priv->i2s, priv->bpsamp);
+
+  if (priv->audio_mode == ES8388_MODULE_ADC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      reg = es8388_readreg(priv, ES8388_ADCCONTROL4) &
+        (~ES8388_ADCWL_BITMASK);
+      es8388_writereg(priv, ES8388_ADCCONTROL4,
+                      reg | ES8388_ADCWL(bit_config));
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      reg = es8388_readreg(priv, ES8388_DACCONTROL1) &
+        (~ES8388_DACWL_BITMASK);
+      es8388_writereg(priv, ES8388_DACCONTROL1,
+                      reg | ES8388_DACWL(bit_config));
+    }
+
+  audinfo("Datawidth set to %u\n", priv->bpsamp);
+}
+
+/****************************************************************************
+ * Name: es8388_setsamplerate
+ *
+ * Description:
+ *  Sets the sample frequency for the codec ADC/DAC and the I2S driver.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void es8388_setsamplerate(FAR struct es8388_dev_s *priv)
+{
+  DEBUGASSERT(priv && priv->lower);
+
+  uint16_t regval;
+
+  switch (priv->samprate)
+    {
+      case 8000:
+        regval = ES8388_LCLK_DIV_1536;
+        break;
+
+      case 11025:
+      case 12000:
+        regval = ES8388_LCLK_DIV_1024;
+        break;
+
+      case 16000:
+        regval = ES8388_LCLK_DIV_768;
+        break;
+
+      case 22050:
+      case 24000:
+        regval = ES8388_LCLK_DIV_512;
+        break;
+
+      case 32000:
+        regval = ES8388_LCLK_DIV_384;
+        break;
+
+      case 44100:
+      case 48000:
+        regval = ES8388_LCLK_DIV_256;
+        break;
+
+      case 88200:
+      case 96000:
+        regval = ES8388_LCLK_DIV_128;
+        break;
+
+      default:
+        audwarn("ERROR: Sample rate not supported.\n");
+        return;
+    }
+
+  /* es8388_setmclkfrequency needs to be called before I2S_**SAMPLERATE */
+
+  es8388_setmclkfrequency(priv);
+
+  I2S_TXSAMPLERATE(priv->i2s, priv->samprate);
+  I2S_RXSAMPLERATE(priv->i2s, priv->samprate);
+
+  if (priv->audio_mode == ES8388_MODULE_ADC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_ADCCONTROL5, ES8388_ADCFSRATIO(regval));
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL2, ES8388_DACFSRATIO(regval));
+    }
+
+  audinfo("Sample rate set to %d\n", priv->samprate);
+}
+
+/****************************************************************************
+ * Name: es8388_getcaps
+ *
+ * Description:
+ *   Get the audio device capabilities.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   type - The type of query.
+ *   caps - A reference to an audio caps structure.
+ *
+ * Returned Value:
+ *   Length of the caps structure.
+ *
+ ****************************************************************************/
+
+static int es8388_getcaps(FAR struct audio_lowerhalf_s *dev, int type,
+                           FAR struct audio_caps_s *caps)
+{
+  /* Validate the structure */
+
+  DEBUGASSERT(caps && caps->ac_len >= sizeof(struct audio_caps_s));
+  audinfo("getcaps: type=%d ac_type=%d\n", type, caps->ac_type);
+
+  /* Fill in the caller's structure based on requested info */
+
+  caps->ac_format.hw  = 0;
+  caps->ac_controls.w = 0;
+
+  switch (caps->ac_type)
+    {
+      /* Caller is querying for the types of units we support */
+
+      case AUDIO_TYPE_QUERY:
+
+        /* Provide our overall capabilities. The interfacing software
+         * must then call us back for specific info for each capability.
+         */
+
+        caps->ac_channels = 2;       /* Stereo output */
+
+        switch (caps->ac_subtype)
+          {
+            case AUDIO_TYPE_QUERY:
+
+              /* We don't decode any formats! Only something above us in
+               * the audio stream can perform decoding on our behalf.
+               */
+
+              /* The types of audio units we implement */
+
+              caps->ac_controls.b[0] =
+                AUDIO_TYPE_OUTPUT | AUDIO_TYPE_FEATURE |
+                AUDIO_TYPE_PROCESSING;
+              break;
+
+            case AUDIO_FMT_MIDI:
+
+              /* We only support Format 0 */
+
+              caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
+              break;
+
+            default:
+              caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
+              break;
+          }
+
+        break;
+
+      /* Provide capabilities of our OUTPUT unit */
+
+      case AUDIO_TYPE_OUTPUT:
+
+        caps->ac_channels = 2;
+
+        switch (caps->ac_subtype)
+          {
+            case AUDIO_TYPE_QUERY:
+
+              /* Report the Sample rates we support */
+
+              /* 8kHz is hardware dependent */
+
+              caps->ac_controls.b[0] =
+                AUDIO_SAMP_RATE_11K | AUDIO_SAMP_RATE_16K |
+                AUDIO_SAMP_RATE_22K | AUDIO_SAMP_RATE_32K |
+                AUDIO_SAMP_RATE_44K | AUDIO_SAMP_RATE_48K;
+              break;
+
+            case AUDIO_FMT_MP3:
+            case AUDIO_FMT_WMA:
+            case AUDIO_FMT_PCM:
+              break;
+
+            default:
+              break;
+          }
+
+        break;
+
+      /* Provide capabilities of our FEATURE units */
+
+      case AUDIO_TYPE_FEATURE:
+
+        /* If the sub-type is UNDEF, then report the Feature Units we
+         * support.
+         */
+
+        if (caps->ac_subtype == AUDIO_FU_UNDEF)
+          {
+            /* Fill in the ac_controls section with the Feature Units we
+             * have.
+             */
+
+            caps->ac_controls.b[0] = AUDIO_FU_VOLUME;
+            caps->ac_controls.b[1] = AUDIO_FU_BALANCE >> 8;
+          }
+
+        break;
+
+      /* Provide capabilities of our PROCESSING unit */
+
+      case AUDIO_TYPE_PROCESSING:
+
+        switch (caps->ac_subtype)
+          {
+            case AUDIO_PU_UNDEF:
+
+              /* Provide the type of Processing Units we support */
+
+              caps->ac_controls.b[0] = AUDIO_PU_STEREO_EXTENDER;
+              break;
+
+            case AUDIO_PU_STEREO_EXTENDER:
+
+              /* Provide capabilities of our Stereo Extender */
+
+              caps->ac_controls.b[0] =
+                AUDIO_STEXT_ENABLE | AUDIO_STEXT_WIDTH;
+              break;
+
+            default:
+
+              /* Other types of processing uint we don't support */
+
+              break;
+          }
+        break;
+
+      /* All others we don't support */
+
+      default:
+
+        /* Zero out the fields to indicate no support */
+
+        caps->ac_subtype = 0;
+        caps->ac_channels = 0;
+
+        break;
+    }
+
+  /* Return the length of the audio_caps_s struct for validation of
+   * proper Audio device type.
+   */
+
+  return caps->ac_len;
+}
+
+/****************************************************************************
+ * Name: es8388_configure
+ *
+ * Description:
+ *   Configure the audio device for the specified mode of operation.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *   caps - A reference to an audio caps structure.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_configure(FAR struct audio_lowerhalf_s *dev,
+                            FAR void *session,
+                            FAR const struct audio_caps_s *caps)
+#else
+static int es8388_configure(FAR struct audio_lowerhalf_s *dev,
+                            FAR const struct audio_caps_s *caps)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+  int ret = OK;
+
+  DEBUGASSERT(priv != NULL && caps != NULL);
+  audinfo("configure: ac_type: %d\n", caps->ac_type);
+
+  /* Process the configure operation */
+
+  switch (caps->ac_type)
+    {
+    case AUDIO_TYPE_FEATURE:
+      audinfo("  AUDIO_TYPE_FEATURE\n");
+
+      /* Process based on Feature Unit */
+
+      switch (caps->ac_format.hw)
+        {
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+        case AUDIO_FU_VOLUME:
+          {
+            /* Set the volume */
+
+            uint16_t volume = caps->ac_controls.hw[0];
+            audinfo("    Volume: %d\n", volume);
+
+            if (volume >= 0 && volume <= 1000)
+              {
+                es8388_setvolume(priv, volume);
+              }
+            else
+              {
+                ret = -EDOM;
+              }
+          }
+          break;
+#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+        case AUDIO_FU_BALANCE:
+          {
+            /* Set the Balance */
+
+            uint16_t balance = caps->ac_controls.hw[0];
+            audinfo("    Balance: %d\n", balance);
+            if (balance >= 0 && balance <= 1000)
+              {
+                priv->balance = balance;
+                es8388_setvolume(priv, priv->volume);
+              }
+            else
+              {
+                ret = -EDOM;
+              }
+          }
+          break;
+#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
+
+        default:
+          auderr("    ERROR: Unrecognized feature unit\n");
+          ret = -ENOTTY;
+          break;
+        }
+        break;
+
+    case AUDIO_TYPE_OUTPUT:
+      {
+        audinfo("  AUDIO_TYPE_OUTPUT:\n");
+        audinfo("    Number of channels: %u\n", caps->ac_channels);
+        audinfo("    Sample rate:        %u\n", caps->ac_controls.hw[0]);
+        audinfo("    Sample width:       %u\n", caps->ac_controls.b[2]);
+
+        /* Verify that all of the requested values are supported */
+
+        ret = -ERANGE;
+        if (caps->ac_channels != 1 && caps->ac_channels != 2)
+          {
+            auderr("ERROR: Unsupported number of channels: %d\n",
+                   caps->ac_channels);
+            break;
+          }
+
+        if (caps->ac_controls.b[2] != 16 &&
+            caps->ac_controls.b[2] != 18 &&
+            caps->ac_controls.b[2] != 20 &&
+            caps->ac_controls.b[2] != 24 &&
+            caps->ac_controls.b[2] != 32)
+          {
+            auderr("ERROR: Unsupported bits per sample: %d\n",
+                   caps->ac_controls.b[2]);
+            break;
+          }
+
+        /* Save the current stream configuration */
+
+        priv->samprate  = caps->ac_controls.hw[0];
+        priv->nchannels = caps->ac_channels;
+        priv->bpsamp    = caps->ac_controls.b[2];
+
+        es8388_setsamplerate(priv);
+        es8388_setbitspersample(priv);
+
+        ret = OK;
+      }
+      break;
+
+    case AUDIO_TYPE_PROCESSING:
+      break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_shutdown
+ *
+ * Description:
+ *   Shutdown the ES8388 chip and reset it.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *
+ * Returned Value:
+ *   Returns OK.
+ *
+ ****************************************************************************/
+
+static int es8388_shutdown(FAR struct audio_lowerhalf_s *dev)
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+
+  DEBUGASSERT(priv);
+
+  audinfo("Shutdown triggered\n");
+
+  /* Now issue a software reset. This puts all ES8388 registers back in
+   * their default state.
+   */
+
+  es8388_reset(priv);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: es8388_senddone
+ *
+ * Description:
+ *   This is the I2S callback function that is invoked when the transfer
+ *   completes.
+ *
+ * Input Parameters:
+ *   i2s - A reference to the I2S interface.
+ *   apb - A reference to the audio pipeline buffer.
+ *   arg - A void reference to the driver state structure.
+ *   result - The result of the last transfer.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void  es8388_senddone(FAR struct i2s_dev_s *i2s,
+                             FAR struct ap_buffer_s *apb, FAR void *arg,
+                             int result)
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)arg;
+  struct audio_msg_s msg;
+  irqstate_t flags;
+  int ret;
+
+  DEBUGASSERT(i2s && priv && priv->running && apb);
+  audinfo("senddone: apb=%p inflight=%d result=%d\n",
+          apb, priv->inflight, result);
+
+  /* We do not place any restriction on the context in which this function
+   * is called. It may be called from an interrupt handler. Therefore, the
+   * doneq and in-flight values might be accessed from the interrupt level.
+   * Not the best design. But we will use interrupt controls to protect
+   * against that possibility.
+   */
+
+  flags = enter_critical_section();
+
+  /* Add the completed buffer to the end of our doneq. We do not yet
+   * decrement the reference count.
+   */
+
+  dq_addlast((FAR dq_entry_t *)apb, &priv->doneq);
+
+  /* And decrement the number of buffers in-flight */
+
+  DEBUGASSERT(priv->inflight > 0);
+  priv->inflight--;
+
+  /* Save the result of the transfer */
+
+  priv->result = result;
+  leave_critical_section(flags);
+
+  /* Now send a message to the worker thread, informing it that there are
+   * buffers in the done queue that need to be cleaned up.
+   */
+
+  msg.msg_id = AUDIO_MSG_COMPLETE;
+  ret = file_mq_send(&priv->mq, (FAR const char *)&msg, sizeof(msg),
+                     CONFIG_ES8388_MSG_PRIO);
+  if (ret < 0)
+    {
+      auderr("ERROR: file_mq_send failed: %d\n", ret);
+    }
+}
+
+/****************************************************************************
+ * Name: es8388_returnbuffers
+ *
+ * Description:
+ *   This function is called after the completion of one or more data
+ *   transfers. This function will empty the done queue and release our
+ *   reference to each buffer.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void es8388_returnbuffers(FAR struct es8388_dev_s *priv)
+{
+  FAR struct ap_buffer_s *apb;
+  irqstate_t flags;
+
+  /* The doneq and in-flight values might be accessed from the interrupt
+   * level in some implementations. Not the best design. But we will
+   * use interrupt controls to protect against that possibility.
+   */
+
+  flags = enter_critical_section();
+  while (dq_peek(&priv->doneq) != NULL)
+    {
+      /* Take the next buffer from the queue of completed transfers */
+
+      apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->doneq);
+      leave_critical_section(flags);
+
+      audinfo("Returning: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
+              apb, apb->curbyte, apb->nbytes, apb->flags);
+
+      /* Are we returning the final buffer in the stream? */
+
+      if ((apb->flags & AUDIO_APB_FINAL) != 0)
+        {
+          /* Both the pending and the done queues should be empty and there
+           * should be no buffers in-flight.
+           */
+
+          DEBUGASSERT(dq_empty(&priv->doneq) && dq_empty(&priv->pendq) &&
+                      priv->inflight == 0);
+
+          /* Set the terminating flag. This will, eventually, cause the
+           * worker thread to exit (if it is not already terminating).
+           */
+
+          audinfo("Terminating\n");
+          priv->terminating = true;
+        }
+
+      /* Release our reference to the audio buffer */
+
+      apb_free(apb);
+
+      /* Send the buffer back up to the previous level. */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+      priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
+#else
+      priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
+#endif
+      flags = enter_critical_section();
+    }
+
+  leave_critical_section(flags);
+}
+
+/****************************************************************************
+ * Name: es8388_sendbuffer
+ *
+ * Description:
+ *   Start the transfer an audio buffer to the ES8388 via I2S. This
+ *   will not wait for the transfer to complete but will return immediately.
+ *   the es8388_senddone called will be invoked when the transfer
+ *   completes, stimulating the worker thread to call this function again.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int es8388_sendbuffer(FAR struct es8388_dev_s *priv)
+{
+  FAR struct ap_buffer_s *apb;
+  irqstate_t flags;
+  uint32_t timeout;
+  int shift;
+  int ret;
+
+  /* Loop while there are audio buffers to be sent and we have few than
+   * CONFIG_ES8388_INFLIGHT then "in-flight"
+   *
+   * The 'inflight' value might be modified from the interrupt level in some
+   * implementations. We will use interrupt controls to protect against
+   * that possibility.
+   *
+   * The 'pendq', on the other hand, is protected via a mutex. Let's
+   * hold the mutex while we are busy here and disable the interrupts
+   * only while accessing 'inflight'.
+   */
+
+  ret = nxmutex_lock(&priv->pendlock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  while (priv->inflight < CONFIG_ES8388_INFLIGHT &&
+         dq_peek(&priv->pendq) != NULL && !priv->paused)
+    {
+      /* Take next buffer from the queue of pending transfers */
+
+      apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq);
+      audinfo("Sending apb=%p, size=%d inflight=%d\n",
+              apb, apb->nbytes, priv->inflight);
+
+      /* Increment the number of buffers in-flight before sending in order
+       * to avoid a possible race condition.
+       */
+
+      flags = enter_critical_section();
+      priv->inflight++;
+      leave_critical_section(flags);
+
+      /* Send the entire audio buffer via I2S. What is a reasonable timeout
+       * to use? This would depend on the bit rate and size of the buffer.
+       *
+       * Samples in the buffer (samples):
+       *   = buffer_size * 8 / bpsamp                           samples
+       * Sample rate (samples/second):
+       *   = samplerate * nchannels
+       * Expected transfer time (seconds):
+       *   = (buffer_size * 8) / bpsamp / samplerate / nchannels
+       *
+       * We will set the timeout about twice that.
+       *
+       * NOTES:
+       * - The multiplier of 8 becomes 16000 for 2x and units of
+       *   milliseconds.
+       * - 16000 is a approximately 16384 (1 << 14).
+       *   nchannels is either (1 << 0) or (1 << 1).
+       *   So this can be simplifies to (milliseconds):
+       *
+       *   = (buffer_size << shift) / bpsamp / samplerate
+       */
+
+      shift = 14;
+
+      shift -= (priv->nchannels > 1) ? 1 : 0;
+
+      timeout = MSEC2TICK(((uint32_t)(apb->nbytes - apb->curbyte) << shift) /
+                          (uint32_t)priv->samprate / (uint32_t)priv->bpsamp);
+
+      ret = I2S_SEND(priv->i2s, apb, es8388_senddone, priv, timeout);
+      if (ret < 0)
+        {
+          auderr("ERROR: I2S_SEND failed: %d\n", ret);
+          break;
+        }
+    }
+
+  nxmutex_unlock(&priv->pendlock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_start
+ *
+ * Description:
+ *   Start the configured operation (audio streaming, volume enabled, etc.).
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_start(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int es8388_start(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+  struct sched_param sparam;
+  struct mq_attr attr;
+  pthread_attr_t tattr;
+  FAR void *value;
+  int ret;
+  uint8_t prev_regval = 0;
+  uint8_t regval = 0;
+
+  audinfo("ES8388 Start\n");
+
+  prev_regval = es8388_readreg(priv, ES8388_DACCONTROL21);
+
+  if (priv->audio_mode == ES8388_MODULE_LINE)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL16,
+                      ES8388_RMIXSEL_RIN2 | ES8388_LMIXSEL_LIN2);
+
+      es8388_writereg(priv, ES8388_DACCONTROL17,
+                      ES8388_LI2LO_ENABLE  |
+                      ES8388_LD2LO_DISABLE |
+                      ES8388_LI2LOVOL(ES8388_MIXER_GAIN_0DB));
+
+      es8388_writereg(priv, ES8388_DACCONTROL20,
+                      ES8388_RI2RO_ENABLE  |
+                      ES8388_RD2RO_DISABLE |
+                      ES8388_RI2ROVOL(ES8388_MIXER_GAIN_0DB));
+
+      es8388_writereg(priv, ES8388_DACCONTROL21,
+                      ES8388_DAC_DLL_PWD_NORMAL |
+                      ES8388_ADC_DLL_PWD_NORMAL |
+                      ES8388_MCLK_DIS_NORMAL    |
+                      ES8388_OFFSET_DIS_DISABLE |
+                      ES8388_SLRCK_SAME         |
+                      ES8388_LRCK_SEL_ADC);
+    }
+  else
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL21,
+                      ES8388_DAC_DLL_PWD_NORMAL |
+                      ES8388_ADC_DLL_PWD_NORMAL |
+                      ES8388_MCLK_DIS_NORMAL    |
+                      ES8388_OFFSET_DIS_DISABLE |
+                      ES8388_SLRCK_SAME         |
+                      ES8388_LRCK_SEL_DAC);
+    }
+
+  regval = es8388_readreg(priv, ES8388_DACCONTROL21);
+
+  if (regval != prev_regval)
+    {
+      es8388_writereg(priv, ES8388_CHIPPOWER,
+                      ES8388_DACVREF_PDN_PWRUP |
+                      ES8388_ADCVREF_PDN_PWRUP |
+                      ES8388_DACDLL_PDN_NORMAL |
+                      ES8388_ADCDLL_PDN_NORMAL |
+                      ES8388_DAC_STM_RST_RESET |
+                      ES8388_ADC_STM_RST_RESET |
+                      ES8388_DAC_DIGPDN_RESET  |
+                      ES8388_ADC_DIGPDN_RESET);
+
+      es8388_writereg(priv, ES8388_CHIPPOWER,
+                      ES8388_DACVREF_PDN_PWRUP  |
+                      ES8388_ADCVREF_PDN_PWRUP  |
+                      ES8388_DACDLL_PDN_NORMAL  |
+                      ES8388_ADCDLL_PDN_NORMAL  |
+                      ES8388_DAC_STM_RST_NORMAL |
+                      ES8388_ADC_STM_RST_NORMAL |
+                      ES8388_DAC_DIGPDN_NORMAL  |
+                      ES8388_ADC_DIGPDN_NORMAL);
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_LINE    ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC)
+    {
+      es8388_writereg(priv, ES8388_ADCPOWER,
+                      ES8388_INT1LP_NORMAL        |
+                      ES8388_FLASHLP_NORMAL       |
+                      ES8388_PDNADCBIASGEN_NORMAL |
+                      ES8388_PDNMICB_PWRON        |
+                      ES8388_PDNADCR_PWRUP        |
+                      ES8388_PDNADCL_PWRUP        |
+                      ES8388_PDNAINR_NORMAL       |
+                      ES8388_PDNAINL_NORMAL);
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_LINE    ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC ||
+      priv->audio_mode == ES8388_MODULE_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACPOWER,
+                      ES8388_ROUT2_ENABLE  |
+                      ES8388_LOUT2_ENABLE  |
+                      ES8388_ROUT1_ENABLE  |
+                      ES8388_LOUT1_ENABLE  |
+                      ES8388_PDNDACR_PWRUP |
+                      ES8388_PDNDACL_PWRUP);
+
+      es8388_setmute(priv, false);
+    }
+
+  /* Create a message queue for the worker thread */
+
+  snprintf(priv->mqname, sizeof(priv->mqname), "/regconfig/%" PRIXPTR,
+           (uintptr_t)priv);
+
+  attr.mq_maxmsg  = 16;
+  attr.mq_msgsize = sizeof(struct audio_msg_s);
+  attr.mq_curmsgs = 0;
+  attr.mq_flags   = 0;
+
+  ret = file_mq_open(&priv->mq, priv->mqname,
+                     O_RDWR | O_CREAT, 0644, &attr);
+  if (ret < 0)
+    {
+      /* Error creating message queue! */
+
+      auderr("ERROR: Couldn't allocate message queue\n");
+      return ret;
+    }
+
+  /* Join any old worker thread we had created to prevent a memory leak */
+
+  if (priv->threadid != 0)
+    {
+      audinfo("Joining old thread\n");
+      pthread_join(priv->threadid, &value);
+    }
+
+  /* Start our thread for sending data to the device */
+
+  pthread_attr_init(&tattr);
+  sparam.sched_priority = sched_get_priority_max(SCHED_FIFO) - 3;
+  pthread_attr_setschedparam(&tattr, &sparam);
+  pthread_attr_setstacksize(&tattr, CONFIG_ES8388_WORKER_STACKSIZE);
+
+  audinfo("Starting worker thread\n");
+  ret = pthread_create(&priv->threadid, &tattr, es8388_workerthread,
+                       (pthread_addr_t)priv);
+  if (ret != OK)
+    {
+      auderr("ERROR: pthread_create failed: %d\n", ret);
+    }
+  else
+    {
+      pthread_setname_np(priv->threadid, "es8388");
+      audinfo("Created worker thread\n");
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_stop
+ *
+ * Description: Stop the configured operation (audio streaming, volume
+ *              disabled, etc.).
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *
+ * Returned Value:
+ *   Returns OK.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_stop(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int es8388_stop(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+  struct audio_msg_s term_msg;
+  FAR void *value;
+
+  audinfo("ES8388 Stop\n");
+
+  if (priv->audio_mode == ES8388_MODULE_LINE)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL21,
+                      ES8388_DAC_DLL_PWD_NORMAL |
+                      ES8388_ADC_DLL_PWD_NORMAL |
+                      ES8388_MCLK_DIS_NORMAL    |
+                      ES8388_OFFSET_DIS_DISABLE |
+                      ES8388_SLRCK_SAME         |
+                      ES8388_LRCK_SEL_DAC);
+
+      es8388_writereg(priv, ES8388_DACCONTROL16,
+                      ES8388_RMIXSEL_RIN1 | ES8388_LMIXSEL_LIN1);
+
+      es8388_writereg(priv, ES8388_DACCONTROL17,
+                      ES8388_LD2LO_ENABLE  |
+                      ES8388_LI2LO_DISABLE |
+                      ES8388_LI2LOVOL(ES8388_MIXER_GAIN_0DB));
+
+      es8388_writereg(priv, ES8388_DACCONTROL20,
+                      ES8388_RD2RO_ENABLE  |
+                      ES8388_RI2RO_DISABLE |
+                      ES8388_RI2ROVOL(ES8388_MIXER_GAIN_0DB));
+
+      goto stop_msg;
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACPOWER,
+                      ES8388_ROUT2_DISABLE |
+                      ES8388_LOUT2_DISABLE |
+                      ES8388_ROUT1_DISABLE |
+                      ES8388_LOUT1_DISABLE |
+                      ES8388_PDNDACR_PWRUP |
+                      ES8388_PDNDACL_PWRUP);
+
+      es8388_setmute(priv, true);
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_ADC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_ADCPOWER,
+                      ES8388_INT1LP_LP        |
+                      ES8388_FLASHLP_LP       |
+                      ES8388_PDNADCBIASGEN_LP |
+                      ES8388_PDNMICB_PWRDN    |
+                      ES8388_PDNADCR_PWRDN    |
+                      ES8388_PDNADCL_PWRDN    |
+                      ES8388_PDNAINR_PWRDN    |
+                      ES8388_PDNAINL_PWRDN);
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL21,
+                      ES8388_DAC_DLL_PWD_PWRDN  |
+                      ES8388_ADC_DLL_PWD_PWRDN  |
+                      ES8388_MCLK_DIS_DISABLE   |
+                      ES8388_OFFSET_DIS_DISABLE |
+                      ES8388_LRCK_SEL_DAC       |
+                      ES8388_SLRCK_SAME);
+    }
+
+stop_msg:
+
+  /* Send a message to stop all audio streaming */
+
+  term_msg.msg_id = AUDIO_MSG_STOP;
+  term_msg.u.data = 0;
+  file_mq_send(&priv->mq, (FAR const char *)&term_msg, sizeof(term_msg),
+               CONFIG_ES8388_MSG_PRIO);
+
+  /* Join the worker thread */
+
+  pthread_join(priv->threadid, &value);
+  priv->threadid = 0;
+
+  es8388_dump_registers(&priv->dev, "After stop");
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: es8388_pause
+ *
+ * Description: Pauses the playback.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *
+ * Returned Value:
+ *   Returns OK.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_pause(FAR struct audio_lowerhalf_s *dev, FAR void *session)
+#else
+static int es8388_pause(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+
+  audinfo("ES8388 Pause\n");
+
+  if (priv->running && !priv->paused)
+    {
+      priv->paused = true;
+      es8388_setmute(priv, true);
+    }
+
+  return OK;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+/****************************************************************************
+ * Name: es8388_resume
+ *
+ * Description: Resumes the playback.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *
+ * Returned Value:
+ *   Returns OK.
+ *
+ ****************************************************************************/
+
+#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_resume(FAR struct audio_lowerhalf_s *dev,
+                         FAR void *session)
+#else
+static int es8388_resume(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+
+  audinfo("ES8388 Resume\n");
+
+  if (priv->running && priv->paused)
+    {
+      priv->paused = false;
+      es8388_setmute(priv, false);
+      es8388_sendbuffer(priv);
+    }
+
+  return OK;
+}
+#endif /* CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME */
+
+/****************************************************************************
+ * Name: es8388_enqueuebuffer
+ *
+ * Description: Enqueue an Audio Pipeline Buffer for playback/ processing.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   apb - A reference to the audio pipeline buffer.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int es8388_enqueuebuffer(FAR struct audio_lowerhalf_s *dev,
+                                FAR struct ap_buffer_s *apb)
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+  struct audio_msg_s term_msg;
+  int ret;
+
+  audinfo("Enqueueing: apb=%p curbyte=%d nbytes=%d flags=%04x\n",
+          apb, apb->curbyte, apb->nbytes, apb->flags);
+
+  ret = nxmutex_lock(&priv->pendlock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Take a reference on the new audio buffer */
+
+  apb_reference(apb);
+
+  /* Add the new buffer to the tail of pending audio buffers */
+
+  apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
+  dq_addlast(&apb->dq_entry, &priv->pendq);
+  nxmutex_unlock(&priv->pendlock);
+
+  /* Send a message to the worker thread indicating that a new buffer has
+   * been enqueued. If mq is NULL, then the playing has not yet started.
+   * In that case we are just "priming the pump" and we don't need to send
+   * any message.
+   */
+
+  ret = OK;
+  if (priv->mq.f_inode != NULL)
+    {
+      term_msg.msg_id  = AUDIO_MSG_ENQUEUE;
+      term_msg.u.data = 0;
+
+      ret = file_mq_send(&priv->mq, (FAR const char *)&term_msg,
+                         sizeof(term_msg), CONFIG_ES8388_MSG_PRIO);
+      if (ret < 0)
+        {
+          auderr("ERROR: file_mq_send failed: %d\n", ret);
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_cancelbuffer
+ *
+ * Description: Called when an enqueued buffer is being cancelled.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   apb - A reference to the audio pipeline buffer.
+ *
+ * Returned Value:
+ *   Returns OK.
+ *
+ ****************************************************************************/
+
+static int es8388_cancelbuffer(FAR struct audio_lowerhalf_s *dev,
+                               FAR struct ap_buffer_s *apb)
+{
+  audinfo("Cancelled apb=%p\n", apb);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: es8388_ioctl
+ *
+ * Description: Perform a device IOCTL.
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   cmd - The IOCTL command.
+ *   arg - The argument of the IOCTL command.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int es8388_ioctl(FAR struct audio_lowerhalf_s *dev, int cmd,
+                        unsigned long arg)
+{
+  int ret = OK;
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+  FAR struct ap_buffer_info_s *bufinfo;
+#endif
+
+  /* Deal with ioctls passed from the upper-half driver */
+
+  switch (cmd)
+    {
+       /* Report our preferred buffer size and quantity */
+
+#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
+      case AUDIOIOC_GETBUFFERINFO:
+        {
+          audinfo("AUDIOIOC_GETBUFFERINFO:\n");
+          bufinfo              = (FAR struct ap_buffer_info_s *) arg;
+          bufinfo->buffer_size = CONFIG_ES8388_BUFFER_SIZE;
+          bufinfo->nbuffers    = CONFIG_ES8388_NUM_BUFFERS;
+        }
+        break;
+#endif
+
+      default:
+        ret = -ENOTTY;
+        audwarn("IOCTL not available\n");
+        break;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_reserve
+ *
+ * Description: Reserves a session (the only one we have).
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_reserve(FAR struct audio_lowerhalf_s *dev,
+                          FAR void **session)
+#else
+static int es8388_reserve(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *) dev;
+  int ret = OK;
+
+  audinfo("ES8388 Reserve\n");
+
+  /* Borrow the APBQ semaphore for thread sync */
+
+  ret = nxmutex_lock(&priv->pendlock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  if (priv->reserved)
+    {
+      ret = -EBUSY;
+    }
+  else
+    {
+      /* Initialize the session context */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+     *session           = NULL;
+#endif
+      priv->inflight    = 0;
+      priv->running     = false;
+      priv->paused      = false;
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+      priv->terminating = false;
+#endif
+      priv->reserved    = true;
+    }
+
+  nxmutex_unlock(&priv->pendlock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_release
+ *
+ * Description: Releases the session (the only one we have).
+ *
+ * Input Parameters:
+ *   dev - A reference to the lower half state structure.
+ *   session - The current audio session.
+ *
+ * Returned Value:
+ *   Returns OK or a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+static int es8388_release(FAR struct audio_lowerhalf_s *dev,
+                          FAR void *session)
+#else
+static int es8388_release(FAR struct audio_lowerhalf_s *dev)
+#endif
+{
+  FAR struct es8388_dev_s *priv = (FAR struct es8388_dev_s *)dev;
+  FAR void *value;
+  int ret;
+
+  audinfo("ES8388 Release\n");
+
+  /* Join any old worker thread we had created to prevent a memory leak */
+
+  if (priv->threadid != 0)
+    {
+      pthread_join(priv->threadid, &value);
+      priv->threadid = 0;
+    }
+
+  /* Borrow the APBQ semaphore for thread sync */
+
+  ret = nxmutex_lock(&priv->pendlock);
+
+  /* Really we should free any queued buffers here */
+
+  priv->reserved = false;
+  nxmutex_unlock(&priv->pendlock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: es8388_audio_output
+ *
+ * Description:
+ *   Initialize and configure the ES8388 device as an audio output device.
+ *   This will be mostly used when adding input support.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   None. No failures are detected.
+ *
+ ****************************************************************************/
+
+static void es8388_audio_output(FAR struct es8388_dev_s *priv)
+{
+  audinfo("ES8388 set to output mode\n");
+
+  priv->audio_mode = ES8388_MODULE_DAC;
+}
+
+/****************************************************************************
+ * Name: es8388_audio_input
+ *
+ * Description:
+ *   Initialize and configure the ES8388 device as an audio input device.
+ *   This will be mostly used when adding input support.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   None. No failures are detected.
+ *
+ ****************************************************************************/
+
+#if 0
+
+static void es8388_audio_input(FAR struct es8388_dev_s *priv)
+{
+  audinfo("ES8388 set to input mode\n");
+
+  priv->audio_mode = ES8388_MODULE_ADC;
+}
+
+#endif
+
+/****************************************************************************
+ * Name: es8388_workerthread
+ *
+ *  This is the thread that feeds data to the chip and keeps the audio
+ *  stream going.
+ *
+ * Input Parameters:
+ *   pvarg - The thread arguments.
+ *
+ * Returned Value:
+ *   Returns NULL.
+ *
+ ****************************************************************************/
+
+static void *es8388_workerthread(pthread_addr_t pvarg)
+{
+  FAR struct es8388_dev_s *priv = (struct es8388_dev_s *) pvarg;
+  struct audio_msg_s msg;
+  FAR struct ap_buffer_s *apb;
+  int msglen;
+  unsigned int prio;
+
+  audinfo("ES8388 worker thread starting\n");
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+  priv->terminating = false;
+#endif
+
+  priv->running = true;
+
+  /* Loop as long as we are supposed to be running and as long as we have
+   * buffers in-flight.
+   */
+
+  while (priv->running || priv->inflight > 0)
+    {
+      /* Check if we have been asked to terminate. We have to check if we
+       * still have buffers in-flight. If we do, then we can't stop until
+       * birds come back to roost.
+       */
+
+      if (priv->terminating && priv->inflight <= 0)
+        {
+          /* We are IDLE. Break out of the loop and exit. */
+
+          break;
+        }
+      else
+        {
+          /* Check if we can send more audio buffers to the ES8388 */
+
+          es8388_sendbuffer(priv);
+        }
+
+      /* Wait for messages from our message queue */
+
+      msglen = file_mq_receive(&priv->mq, (FAR char *)&msg,
+                               sizeof(msg), &prio);
+
+      /* Handle the case when we return with no message */
+
+      if (msglen < sizeof(struct audio_msg_s))
+        {
+          auderr("ERROR: Message too small: %d\n", msglen);
+          continue;
+        }
+
+      /* Process the message */
+
+      switch (msg.msg_id)
+        {
+          /* The ISR has requested more data. We will catch this case at
+           * the top of the loop.
+           */
+
+          case AUDIO_MSG_DATA_REQUEST:
+            audinfo("AUDIO_MSG_DATA_REQUEST\n");
+            break;
+
+          /* Stop the playback */
+
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+          case AUDIO_MSG_STOP:
+
+            /* Indicate that we are terminating */
+
+            audinfo("AUDIO_MSG_STOP: Terminating\n");
+            priv->terminating = true;
+            break;
+#endif
+
+          /* We have a new buffer to send. We will catch this case at
+           * the top of the loop.
+           */
+
+          case AUDIO_MSG_ENQUEUE:
+            audinfo("AUDIO_MSG_ENQUEUE\n");
+            break;
+
+          /* We will wake up from the I2S callback with this message */
+
+          case AUDIO_MSG_COMPLETE:
+            audinfo("AUDIO_MSG_COMPLETE\n");
+            es8388_returnbuffers(priv);
+            break;
+
+          default:
+            auderr("ERROR: Ignoring message ID %d\n", msg.msg_id);
+            break;
+        }
+    }
+
+  /* Reset the ES8388 hardware */
+
+  es8388_reset(priv);
+
+  /* Return any pending buffers in our pending queue */
+
+  nxmutex_lock(&priv->pendlock);
+  while ((apb = (FAR struct ap_buffer_s *)dq_remfirst(&priv->pendq)) != NULL)
+    {
+      /* Release our reference to the buffer */
+
+      apb_free(apb);
+
+      /* Send the buffer back up to the previous level. */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+      priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK, NULL);
+#else
+      priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
+#endif
+    }
+
+  nxmutex_unlock(&priv->pendlock);
+
+  /* Return any pending buffers in our done queue */
+
+  es8388_returnbuffers(priv);
+
+  /* Close the message queue */
+
+  file_mq_close(&priv->mq);
+  file_mq_unlink(priv->mqname);
+
+  /* Send an AUDIO_MSG_COMPLETE message to the client */
+
+#ifdef CONFIG_AUDIO_MULTI_SESSION
+  priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK, NULL);
+#else
+  priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
+#endif
+
+  audinfo("ES8388 worker thread finishing\n");
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: es8388_reset
+ *
+ * Description:
+ *   Reset and re-initialize the ES8388.
+ *
+ * Input Parameters:
+ *   priv - A reference to the driver state structure.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+static void es8388_reset(FAR struct es8388_dev_s *priv)
+{
+  /* Put audio output back to its initial configuration */
+
+  audinfo("ES8388 Reset\n");
+
+  priv->dac_output = ES8388_DAC_OUTPUT_ALL;
+  priv->adc_input  = ES8388_ADC_INPUT_ALL;
+  priv->samprate   = ES8388_DEFAULT_SAMPRATE;
+  priv->nchannels  = ES8388_DEFAULT_NCHANNELS;
+  priv->bpsamp     = ES8388_DEFAULT_BPSAMP;
+#if !defined(CONFIG_AUDIO_EXCLUDE_VOLUME) && !defined(CONFIG_AUDIO_EXCLUDE_BALANCE)
+  priv->balance    = 500;            /* Center balance */
+#endif
+
+  /* Software reset. This puts all ES8388 registers back in their
+   * default state.
+   */
+
+  uint8_t regconfig;
+
+  es8388_audio_output(priv);
+
+  es8388_writereg(priv, ES8388_DACCONTROL3,
+                  ES8388_DACMUTE_MUTED       |
+                  ES8388_DACLER_NORMAL       |
+                  ES8388_DACSOFTRAMP_DISABLE |
+                  ES8388_DACRAMPRATE_4LRCK);
+
+  es8388_writereg(priv, ES8388_CONTROL2,
+                  ES8388_PDNVREFBUF_NORMAL  |
+                  ES8388_VREFLO_NORMAL      |
+                  ES8388_PDNIBIASGEN_NORMAL |
+                  ES8388_PDNANA_NORMAL      |
+                  ES8388_LPVREFBUF_LP       |
+                  ES8388_LPVCMMOD_NORMAL    |
+                  (1 << 6)); /* Default value of undocumented bit */
+
+  es8388_writereg(priv, ES8388_CHIPPOWER,
+                  ES8388_DACVREF_PDN_SHIFT  |
+                  ES8388_ADCVREF_PDN_PWRUP  |
+                  ES8388_DACDLL_PDN_NORMAL  |
+                  ES8388_ADCDLL_PDN_NORMAL  |
+                  ES8388_DAC_STM_RST_NORMAL |
+                  ES8388_ADC_STM_RST_NORMAL |
+                  ES8388_DAC_DIGPDN_NORMAL  |
+                  ES8388_ADC_DIGPDN_NORMAL);
+
+  es8388_writereg(priv, ES8388_MASTERMODE,
+                  ES8388_BCLKDIV(ES8388_MCLK_DIV_AUTO) |
+                  ES8388_BCLK_INV_NORMAL               |
+                  ES8388_MCLKDIV2_NODIV                |
+                  ES8388_MSC_SLAVE);
+
+  es8388_writereg(priv, ES8388_DACPOWER,
+                  ES8388_ROUT2_DISABLE |
+                  ES8388_LOUT2_DISABLE |
+                  ES8388_ROUT1_DISABLE |
+                  ES8388_LOUT1_DISABLE |
+                  ES8388_PDNDACR_PWRDN |
+                  ES8388_PDNDACL_PWRDN);
+
+  es8388_writereg(priv, ES8388_DACCONTROL1,
+                  ES8388_DACFORMAT(ES8388_I2S_NORMAL)     |
+                  ES8388_DACWL(ES8388_WORD_LENGTH_16BITS) |
+                  ES8388_DACLRP_NORM_2ND                  |
+                  ES8388_DACLRSWAP_NORMAL);
+
+  es8388_writereg(priv, ES8388_DACCONTROL2,
+                  ES8388_DACFSRATIO(ES8388_LCLK_DIV_256) |
+                  ES8388_DACFSMODE_SINGLE);
+
+  es8388_writereg(priv, ES8388_DACCONTROL16,
+                  ES8388_RMIXSEL_RIN1 | ES8388_LMIXSEL_LIN1);
+
+  es8388_writereg(priv, ES8388_DACCONTROL17,
+                  ES8388_LD2LO_ENABLE  |
+                  ES8388_LI2LO_DISABLE |
+                  ES8388_LI2LOVOL(ES8388_MIXER_GAIN_0DB));
+
+  es8388_writereg(priv, ES8388_DACCONTROL20,
+                  ES8388_RD2RO_ENABLE  |
+                  ES8388_RI2RO_DISABLE |
+                  ES8388_RI2ROVOL(ES8388_MIXER_GAIN_0DB));
+
+  es8388_writereg(priv, ES8388_DACCONTROL21,
+                  ES8388_DAC_DLL_PWD_NORMAL |
+                  ES8388_ADC_DLL_PWD_NORMAL |
+                  ES8388_MCLK_DIS_NORMAL    |
+                  ES8388_OFFSET_DIS_DISABLE |
+                  ES8388_SLRCK_SAME         |
+                  ES8388_LRCK_SEL_DAC);
+
+  es8388_writereg(priv, ES8388_DACCONTROL23, ES8388_VROI_1_5K);
+
+  es8388_writereg(priv, ES8388_DACCONTROL24,
+                  ES8388_LOUT1VOL(ES8388_DAC_CHVOL_DB(0)));
+
+  es8388_writereg(priv, ES8388_DACCONTROL25,
+                  ES8388_ROUT1VOL(ES8388_DAC_CHVOL_DB(0)));
+
+  es8388_writereg(priv, ES8388_DACCONTROL26,
+                  ES8388_LOUT2VOL(ES8388_DAC_CHVOL_DB(0)));
+
+  es8388_writereg(priv, ES8388_DACCONTROL27,
+                  ES8388_ROUT2VOL(ES8388_DAC_CHVOL_DB(0)));
+
+  es8388_setmute(priv, true);
+
+  regconfig = 0;
+
+  if (priv->dac_output == ES8388_DAC_OUTPUT_LINE2)
+    {
+      regconfig = ES8388_DAC_CHANNEL_LOUT1 | ES8388_DAC_CHANNEL_ROUT1;
+    }
+  else if (priv->dac_output == ES8388_DAC_OUTPUT_LINE1)
+    {
+      regconfig = ES8388_DAC_CHANNEL_LOUT2 | ES8388_DAC_CHANNEL_ROUT2;
+    }
+  else
+    {
+      regconfig = ES8388_DAC_CHANNEL_LOUT1 | ES8388_DAC_CHANNEL_ROUT1 |
+            ES8388_DAC_CHANNEL_LOUT2 | ES8388_DAC_CHANNEL_ROUT2;
+    }
+
+  es8388_writereg(priv, ES8388_DACPOWER, regconfig);
+
+  es8388_writereg(priv, ES8388_ADCPOWER,
+                  ES8388_INT1LP_LP        |
+                  ES8388_FLASHLP_LP       |
+                  ES8388_PDNADCBIASGEN_LP |
+                  ES8388_PDNMICB_PWRDN    |
+                  ES8388_PDNADCR_PWRDN    |
+                  ES8388_PDNADCL_PWRDN    |
+                  ES8388_PDNAINR_PWRDN    |
+                  ES8388_PDNAINL_PWRDN);
+
+  es8388_writereg(priv, ES8388_ADCCONTROL1,
+                  ES8388_MICAMPR(ES8388_MIC_GAIN_0DB) |
+                  ES8388_MICAMPL(ES8388_MIC_GAIN_0DB));
+
+  regconfig = 0;
+
+  if (priv->adc_input == ES8388_ADC_INPUT_LINE1)
+    {
+      regconfig = ES8388_ADC_CHANNEL_LINPUT1_RINPUT1;
+    }
+  else if (priv->adc_input == ES8388_ADC_INPUT_LINE2)
+    {
+      regconfig = ES8388_ADC_CHANNEL_LINPUT2_RINPUT2;
+    }
+  else
+    {
+      regconfig = ES8388_ADC_CHANNEL_DIFFERENCE;
+    }
+
+  es8388_writereg(priv, ES8388_ADCCONTROL2, regconfig);
+
+  es8388_writereg(priv, ES8388_ADCCONTROL3,
+                  ES8388_TRI_NORMAL         |
+                  ES8388_MONOMIX_STEREO     |
+                  ES8388_DS_LINPUT1_RINPUT1 |
+                  (1 << 1)); /* Default value of undocumented bit */
+
+  es8388_writereg(priv, ES8388_ADCCONTROL4,
+                  ES8388_ADCFORMAT(ES8388_I2S_NORMAL)     |
+                  ES8388_ADCWL(ES8388_WORD_LENGTH_16BITS) |
+                  ES8388_ADCLRP_NORM_2ND                  |
+                  ES8388_DATSEL_LL);
+
+  es8388_writereg(priv, ES8388_ADCCONTROL5,
+                  ES8388_ADCFSRATIO(ES8388_LCLK_DIV_256) |
+                  ES8388_ADCFSMODE_SINGLE);
+
+  es8388_writereg(priv, ES8388_ADCPOWER,
+                  ES8388_INT1LP_LP            |
+                  ES8388_FLASHLP_NORMAL       |
+                  ES8388_PDNADCBIASGEN_NORMAL |
+                  ES8388_PDNMICB_PWRDN        |
+                  ES8388_PDNADCR_PWRUP        |
+                  ES8388_PDNADCL_PWRUP        |
+                  ES8388_PDNAINR_NORMAL       |
+                  ES8388_PDNAINL_NORMAL);
+
+  /* Stop sequence to avoid noise at boot */
+
+  if (priv->audio_mode == ES8388_MODULE_LINE)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL21,
+                      ES8388_DAC_DLL_PWD_NORMAL |
+                      ES8388_ADC_DLL_PWD_NORMAL |
+                      ES8388_MCLK_DIS_NORMAL    |
+                      ES8388_OFFSET_DIS_DISABLE |
+                      ES8388_SLRCK_SAME         |
+                      ES8388_LRCK_SEL_DAC);
+
+      es8388_writereg(priv, ES8388_DACCONTROL16,
+                      ES8388_RMIXSEL_RIN1 | ES8388_LMIXSEL_LIN1);
+
+      es8388_writereg(priv, ES8388_DACCONTROL17,
+                      ES8388_LD2LO_ENABLE  |
+                      ES8388_LI2LO_DISABLE |
+                      ES8388_LI2LOVOL(ES8388_MIXER_GAIN_0DB));
+
+      es8388_writereg(priv, ES8388_DACCONTROL20,
+                      ES8388_RD2RO_ENABLE  |
+                      ES8388_RI2RO_DISABLE |
+                      ES8388_RI2ROVOL(ES8388_MIXER_GAIN_0DB));
+
+      goto reset_finish;
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_DAC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACPOWER,
+                      ES8388_ROUT2_DISABLE |
+                      ES8388_LOUT2_DISABLE |
+                      ES8388_ROUT1_DISABLE |
+                      ES8388_LOUT1_DISABLE |
+                      ES8388_PDNDACR_PWRUP |
+                      ES8388_PDNDACL_PWRUP);
+
+      es8388_setmute(priv, true);
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_ADC ||
+      priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_ADCPOWER,
+                      ES8388_INT1LP_LP        |
+                      ES8388_FLASHLP_LP       |
+                      ES8388_PDNADCBIASGEN_LP |
+                      ES8388_PDNMICB_PWRDN    |
+                      ES8388_PDNADCR_PWRDN    |
+                      ES8388_PDNADCL_PWRDN    |
+                      ES8388_PDNAINR_PWRDN    |
+                      ES8388_PDNAINL_PWRDN);
+    }
+
+  if (priv->audio_mode == ES8388_MODULE_ADC_DAC)
+    {
+      es8388_writereg(priv, ES8388_DACCONTROL21,
+                      ES8388_DAC_DLL_PWD_PWRDN  |
+                      ES8388_ADC_DLL_PWD_PWRDN  |
+                      ES8388_MCLK_DIS_DISABLE   |
+                      ES8388_OFFSET_DIS_DISABLE |
+                      ES8388_LRCK_SEL_DAC       |
+                      ES8388_SLRCK_SAME);
+    }
+
+reset_finish:
+
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+  es8388_setvolume(priv, CONFIG_ES8388_OUTPUT_INITVOLUME);
+#endif
+
+  es8388_dump_registers(&priv->dev, "After reset");
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: es8388_initialize
+ *
+ * Description:
+ *   Initialize the ES8388 device.
+ *
+ * Input Parameters:
+ *   i2c     - An I2C driver instance.
+ *   i2s     - An I2S driver instance.
+ *   lower   - Persistent board configuration data.
+ *
+ * Returned Value:
+ *   A new lower half audio interface for the ES8388 device is returned on
+ *   success; NULL is returned on failure.
+ *
+ ****************************************************************************/
+
+FAR struct audio_lowerhalf_s *
+  es8388_initialize(FAR struct i2c_master_s *i2c,
+                    FAR struct i2s_dev_s *i2s,
+                    FAR const struct es8388_lower_s *lower)
+{
+  FAR struct es8388_dev_s *priv;
+
+  audinfo("Initializing ES8388\n");
+
+  /* Sanity check */
+
+  DEBUGASSERT(i2c && i2s && lower);
+
+  /* Allocate a ES8388 device structure */
+
+  priv = (FAR struct es8388_dev_s *)kmm_zalloc(sizeof(struct es8388_dev_s));
+
+  if (priv)
+    {
+      priv->dev.ops    = &g_audioops;
+      priv->lower      = lower;
+      priv->i2c        = i2c;
+      priv->i2s        = i2s;
+
+      nxmutex_init(&priv->pendlock);
+      dq_init(&priv->pendq);
+      dq_init(&priv->doneq);
+
+      audinfo("ES8388: address=%02x frequency=%" PRId32 "\n",
+              lower->address, lower->frequency);
+
+      /* Reset and reconfigure the ES8388 hardware */
+
+      es8388_dump_registers(&priv->dev, "Before reset");
+
+      es8388_reset(priv);
+      return &priv->dev;
+    }
+
+  nxmutex_destroy(&priv->pendlock);
+  kmm_free(priv);
+  return NULL;
+}
diff --git a/drivers/audio/es8388.h b/drivers/audio/es8388.h
new file mode 100644
index 0000000000..17b4c68e55
--- /dev/null
+++ b/drivers/audio/es8388.h
@@ -0,0 +1,1179 @@
+/****************************************************************************
+ * drivers/audio/es8388.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __DRIVERS_AUDIO_ES8388_H
+#define __DRIVERS_AUDIO_ES8388_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/compiler.h>
+
+#include <pthread.h>
+
+#include <nuttx/mqueue.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/fs/ioctl.h>
+
+#ifdef CONFIG_AUDIO
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ES8388_DAC_CHVOL_DB(v) (2*v/3 + 30)
+
+/* Registers Addresses ******************************************************/
+
+#define ES8388_CONTROL1                0x00
+#define ES8388_CONTROL2                0x01
+#define ES8388_CHIPPOWER               0x02
+#define ES8388_ADCPOWER                0x03
+#define ES8388_DACPOWER                0x04
+#define ES8388_CHIPLOPOW1              0x05
+#define ES8388_CHIPLOPOW2              0x06
+#define ES8388_ANAVOLMANAG             0x07
+#define ES8388_MASTERMODE              0x08
+#define ES8388_ADCCONTROL1             0x09
+#define ES8388_ADCCONTROL2             0x0a
+#define ES8388_ADCCONTROL3             0x0b
+#define ES8388_ADCCONTROL4             0x0c
+#define ES8388_ADCCONTROL5             0x0d
+#define ES8388_ADCCONTROL6             0x0e
+#define ES8388_ADCCONTROL7             0x0f
+#define ES8388_ADCCONTROL8             0x10
+#define ES8388_ADCCONTROL9             0x11
+#define ES8388_ADCCONTROL10            0x12
+#define ES8388_ADCCONTROL11            0x13
+#define ES8388_ADCCONTROL12            0x14
+#define ES8388_ADCCONTROL13            0x15
+#define ES8388_ADCCONTROL14            0x16
+#define ES8388_DACCONTROL1             0x17
+#define ES8388_DACCONTROL2             0x18
+#define ES8388_DACCONTROL3             0x19
+#define ES8388_DACCONTROL4             0x1a
+#define ES8388_DACCONTROL5             0x1b
+#define ES8388_DACCONTROL6             0x1c
+#define ES8388_DACCONTROL7             0x1d
+#define ES8388_DACCONTROL8             0x1e
+#define ES8388_DACCONTROL9             0x1f
+#define ES8388_DACCONTROL10            0x20
+#define ES8388_DACCONTROL11            0x21
+#define ES8388_DACCONTROL12            0x22
+#define ES8388_DACCONTROL13            0x23
+#define ES8388_DACCONTROL14            0x24
+#define ES8388_DACCONTROL15            0x25
+#define ES8388_DACCONTROL16            0x26
+#define ES8388_DACCONTROL17            0x27
+#define ES8388_DACCONTROL18            0x28
+#define ES8388_DACCONTROL19            0x29
+#define ES8388_DACCONTROL20            0x2a
+#define ES8388_DACCONTROL21            0x2b
+#define ES8388_DACCONTROL22            0x2c
+#define ES8388_DACCONTROL23            0x2d
+#define ES8388_DACCONTROL24            0x2e
+#define ES8388_DACCONTROL25            0x2f
+#define ES8388_DACCONTROL26            0x30
+#define ES8388_DACCONTROL27            0x31
+#define ES8388_DACCONTROL28            0x32
+#define ES8388_DACCONTROL29            0x33
+#define ES8388_DACCONTROL30            0x34
+
+/* Register Power-up Default Values *****************************************/
+
+#define ES8388_CONTROL1_DEFAULT        0x06
+#define ES8388_CONTROL2_DEFAULT        0x5c
+#define ES8388_CHIPPOWER_DEFAULT       0xc3
+#define ES8388_ADCPOWER_DEFAULT        0xfc
+#define ES8388_DACPOWER_DEFAULT        0xc0
+#define ES8388_CHIPLOPOW1_DEFAULT      0x00
+#define ES8388_CHIPLOPOW2_DEFAULT      0x00
+#define ES8388_ANAVOLMANAG_DEFAULT     0x7c
+#define ES8388_MASTERMODE_DEFAULT      0x80
+#define ES8388_ADCCONTROL1_DEFAULT     0x00
+#define ES8388_ADCCONTROL2_DEFAULT     0x00
+#define ES8388_ADCCONTROL3_DEFAULT     0x02
+#define ES8388_ADCCONTROL4_DEFAULT     0x00
+#define ES8388_ADCCONTROL5_DEFAULT     0x06
+#define ES8388_ADCCONTROL6_DEFAULT     0x30
+#define ES8388_ADCCONTROL7_DEFAULT     0x20
+#define ES8388_ADCCONTROL8_DEFAULT     0xc0
+#define ES8388_ADCCONTROL9_DEFAULT     0xc0
+#define ES8388_ADCCONTROL10_DEFAULT    0x38
+#define ES8388_ADCCONTROL11_DEFAULT    0xb0
+#define ES8388_ADCCONTROL12_DEFAULT    0x32
+#define ES8388_ADCCONTROL13_DEFAULT    0x06
+#define ES8388_ADCCONTROL14_DEFAULT    0x00
+#define ES8388_DACCONTROL1_DEFAULT     0x00
+#define ES8388_DACCONTROL2_DEFAULT     0x06
+#define ES8388_DACCONTROL3_DEFAULT     0x22
+#define ES8388_DACCONTROL4_DEFAULT     0xc0
+#define ES8388_DACCONTROL5_DEFAULT     0xc0
+#define ES8388_DACCONTROL6_DEFAULT     0x08
+#define ES8388_DACCONTROL7_DEFAULT     0x00
+#define ES8388_DACCONTROL8_DEFAULT     0x1f
+#define ES8388_DACCONTROL9_DEFAULT     0xf7
+#define ES8388_DACCONTROL10_DEFAULT    0xfd
+#define ES8388_DACCONTROL11_DEFAULT    0xff
+#define ES8388_DACCONTROL12_DEFAULT    0x1f
+#define ES8388_DACCONTROL13_DEFAULT    0xf7
+#define ES8388_DACCONTROL14_DEFAULT    0xfd
+#define ES8388_DACCONTROL15_DEFAULT    0xff
+#define ES8388_DACCONTROL16_DEFAULT    0x00
+#define ES8388_DACCONTROL17_DEFAULT    0x38
+#define ES8388_DACCONTROL18_DEFAULT    0x28
+#define ES8388_DACCONTROL19_DEFAULT    0x28
+#define ES8388_DACCONTROL20_DEFAULT    0x38
+#define ES8388_DACCONTROL21_DEFAULT    0x00
+#define ES8388_DACCONTROL22_DEFAULT    0x00
+#define ES8388_DACCONTROL23_DEFAULT    0x00
+#define ES8388_DACCONTROL24_DEFAULT    0x00
+#define ES8388_DACCONTROL25_DEFAULT    0x00
+#define ES8388_DACCONTROL26_DEFAULT    0x00
+#define ES8388_DACCONTROL27_DEFAULT    0x00
+#define ES8388_DACCONTROL28_DEFAULT    0x00
+#define ES8388_DACCONTROL29_DEFAULT    0xaa
+#define ES8388_DACCONTROL30_DEFAULT    0xaa
+
+/* Register Bit Definitions *************************************************/
+
+/* 0x00 Chip Control 1 */
+
+#define ES8388_VMIDSEL_SHIFT          (0)
+#define ES8388_VMIDSEL_BITMASK        (0x03 << ES8388_VMIDSEL_SHIFT)
+#define ES8388_VMIDSEL_DISABLE        (0 << ES8388_VMIDSEL_SHIFT)
+#define ES8388_VMIDSEL_50K            (1 << ES8388_VMIDSEL_SHIFT)
+#define ES8388_VMIDSEL_500K           (2 << ES8388_VMIDSEL_SHIFT)
+#define ES8388_VMIDSEL_5K             (3 << ES8388_VMIDSEL_SHIFT)
+
+#define ES8388_ENREF_SHIFT            (2)
+#define ES8388_ENREF_BITMASK          (0x01 << ES8388_ENREF_SHIFT)
+#define ES8388_ENREF_DISABLE          (0 << ES8388_ENREF_SHIFT)
+#define ES8388_ENREF_ENABLE           (1 << ES8388_ENREF_SHIFT)
+
+#define ES8388_SEQEN_SHIFT            (3)
+#define ES8388_SEQEN_BITMASK          (0x01 << ES8388_SEQEN_SHIFT)
+#define ES8388_SEQEN_DISABLE          (0 << ES8388_SEQEN_SHIFT)
+#define ES8388_SEQEN_ENABLE           (1 << ES8388_SEQEN_SHIFT)
+
+#define ES8388_SAMEFS_SHIFT           (4)
+#define ES8388_SAMEFS_BITMASK         (0x01 << ES8388_SAMEFS_SHIFT)
+#define ES8388_SAMEFS_DIFFER          (0 << ES8388_SAMEFS_SHIFT)
+#define ES8388_SAMEFS_SAME            (1 << ES8388_SAMEFS_SHIFT)
+
+#define ES8388_DACMCLK_SHIFT          (5)
+#define ES8388_DACMCLK_BITMASK        (0x01 << ES8388_DACMCLK_SHIFT)
+#define ES8388_DACMCLK_ADCMCLK        (0 << ES8388_DACMCLK_SHIFT)
+#define ES8388_DACMCLK_DACMCLK        (1 << ES8388_DACMCLK_SHIFT)
+
+#define ES8388_LRCM_SHIFT             (6)
+#define ES8388_LRCM_BITMASK           (0x01 << ES8388_LRCM_SHIFT)
+#define ES8388_LRCM_ISOLATED          (0 << ES8388_LRCM_SHIFT)
+#define ES8388_LRCM_ALL               (1 << ES8388_LRCM_SHIFT)
+
+#define ES8388_SCPRESET_SHIFT         (7)
+#define ES8388_SCPRESET_BITMASK       (0x01 << ES8388_SCPRESET_SHIFT)
+#define ES8388_SCPRESET_NORMAL        (0 << ES8388_SCPRESET_SHIFT)
+#define ES8388_SCPRESET_RESET         (1 << ES8388_SCPRESET_SHIFT)
+
+/* 0x01 Chip Control 2 */
+
+#define ES8388_PDNVREFBUF_SHIFT       (0)
+#define ES8388_PDNVREFBUF_BITMASK     (0x01 << ES8388_PDNVREFBUF_SHIFT)
+#define ES8388_PDNVREFBUF_NORMAL      (0 << ES8388_PDNVREFBUF_SHIFT)
+#define ES8388_PDNVREFBUF_PWRDN       (1 << ES8388_PDNVREFBUF_SHIFT)
+
+#define ES8388_VREFLO_SHIFT           (1)
+#define ES8388_VREFLO_BITMASK         (0x01 << ES8388_VREFLO_SHIFT)
+#define ES8388_VREFLO_NORMAL          (0 << ES8388_VREFLO_SHIFT)
+#define ES8388_VREFLO_LP              (1 << ES8388_VREFLO_SHIFT)
+
+#define ES8388_PDNIBIASGEN_SHIFT      (2)
+#define ES8388_PDNIBIASGEN_BITMASK    (0x01 << ES8388_PDNIBIASGEN_SHIFT)
+#define ES8388_PDNIBIASGEN_NORMAL     (0 << ES8388_PDNIBIASGEN_SHIFT)
+#define ES8388_PDNIBIASGEN_PWRDN      (1 << ES8388_PDNIBIASGEN_SHIFT)
+
+#define ES8388_PDNANA_SHIFT           (3)
+#define ES8388_PDNANA_BITMASK         (0x01 << ES8388_PDNANA_SHIFT)
+#define ES8388_PDNANA_NORMAL          (0 << ES8388_PDNANA_SHIFT)
+#define ES8388_PDNANA_PWRDN           (1 << ES8388_PDNANA_SHIFT)
+
+#define ES8388_LPVREFBUF_SHIFT        (4)
+#define ES8388_LPVREFBUF_BITMASK      (0x01 << ES8388_LPVREFBUF_SHIFT)
+#define ES8388_LPVREFBUF_NORMAL       (0 << ES8388_LPVREFBUF_SHIFT)
+#define ES8388_LPVREFBUF_LP           (1 << ES8388_LPVREFBUF_SHIFT)
+
+#define ES8388_LPVCMMOD_SHIFT         (5)
+#define ES8388_LPVCMMOD_BITMASK       (0x01 << ES8388_LPVCMMOD_SHIFT)
+#define ES8388_LPVCMMOD_NORMAL        (0 << ES8388_LPVCMMOD_SHIFT)
+#define ES8388_LPVCMMOD_LP            (1 << ES8388_LPVCMMOD_SHIFT)
+
+/* 0x02 Chip Power Management */
+
+#define ES8388_DACVREF_PDN_SHIFT      (0)
+#define ES8388_DACVREF_PDN_BITMASK    (0x01 << ES8388_DACVREF_PDN_SHIFT)
+#define ES8388_DACVREF_PDN_PWRUP      (0 << ES8388_DACVREF_PDN_SHIFT)
+#define ES8388_DACVREF_PDN_PWRDN      (1 << ES8388_DACVREF_PDN_SHIFT)
+
+#define ES8388_ADCVREF_PDN_SHIFT      (1)
+#define ES8388_ADCVREF_PDN_BITMASK    (0x01 << ES8388_ADCVREF_PDN_SHIFT)
+#define ES8388_ADCVREF_PDN_PWRUP      (0 << ES8388_ADCVREF_PDN_SHIFT)
+#define ES8388_ADCVREF_PDN_PWRDN      (1 << ES8388_ADCVREF_PDN_SHIFT)
+
+#define ES8388_DACDLL_PDN_SHIFT       (2)
+#define ES8388_DACDLL_PDN_BITMASK     (0x01 << ES8388_DACDLL_PDN_SHIFT)
+#define ES8388_DACDLL_PDN_NORMAL      (0 << ES8388_DACDLL_PDN_SHIFT)
+#define ES8388_DACDLL_PDN_PWRDN       (1 << ES8388_DACDLL_PDN_SHIFT)
+
+#define ES8388_ADCDLL_PDN_SHIFT       (3)
+#define ES8388_ADCDLL_PDN_BITMASK     (0x01 << ES8388_ADCDLL_PDN_SHIFT)
+#define ES8388_ADCDLL_PDN_NORMAL      (0 << ES8388_ADCDLL_PDN_SHIFT)
+#define ES8388_ADCDLL_PDN_PWRDN       (1 << ES8388_ADCDLL_PDN_SHIFT)
+
+#define ES8388_DAC_STM_RST_SHIFT      (4)
+#define ES8388_DAC_STM_RST_BITMASK    (0x01 << ES8388_DAC_STM_RST_SHIFT)
+#define ES8388_DAC_STM_RST_NORMAL     (0 << ES8388_DAC_STM_RST_SHIFT)
+#define ES8388_DAC_STM_RST_RESET      (1 << ES8388_DAC_STM_RST_SHIFT)
+
+#define ES8388_ADC_STM_RST_SHIFT      (5)
+#define ES8388_ADC_STM_RST_BITMASK    (0x01 << ES8388_ADC_STM_RST_SHIFT)
+#define ES8388_ADC_STM_RST_NORMAL     (0 << ES8388_ADC_STM_RST_SHIFT)
+#define ES8388_ADC_STM_RST_RESET      (1 << ES8388_ADC_STM_RST_SHIFT)
+
+#define ES8388_DAC_DIGPDN_SHIFT       (6)
+#define ES8388_DAC_DIGPDN_BITMASK     (0x01 << ES8388_DAC_DIGPDN_SHIFT)
+#define ES8388_DAC_DIGPDN_NORMAL      (0 << ES8388_DAC_DIGPDN_SHIFT)
+#define ES8388_DAC_DIGPDN_RESET       (1 << ES8388_DAC_DIGPDN_SHIFT)
+
+#define ES8388_ADC_DIGPDN_SHIFT       (7)
+#define ES8388_ADC_DIGPDN_BITMASK     (0x01 << ES8388_ADC_DIGPDN_SHIFT)
+#define ES8388_ADC_DIGPDN_NORMAL      (0 << ES8388_ADC_DIGPDN_SHIFT)
+#define ES8388_ADC_DIGPDN_RESET       (1 << ES8388_ADC_DIGPDN_SHIFT)
+
+/* 0x03 ADC Power Management */
+
+#define ES8388_INT1LP_SHIFT           (0)
+#define ES8388_INT1LP_BITMASK         (0x01 << ES8388_INT1LP_SHIFT)
+#define ES8388_INT1LP_NORMAL          (0 << ES8388_INT1LP_SHIFT)
+#define ES8388_INT1LP_LP              (1 << ES8388_INT1LP_SHIFT)
+
+#define ES8388_FLASHLP_SHIFT          (1)
+#define ES8388_FLASHLP_BITMASK        (0x01 << ES8388_FLASHLP_SHIFT)
+#define ES8388_FLASHLP_NORMAL         (0 << ES8388_FLASHLP_SHIFT)
+#define ES8388_FLASHLP_LP             (1 << ES8388_FLASHLP_SHIFT)
+
+#define ES8388_PDNADCBIASGEN_SHIFT    (2)
+#define ES8388_PDNADCBIASGEN_BITMASK  (0x01 << ES8388_PDNADCBIASGEN_SHIFT)
+#define ES8388_PDNADCBIASGEN_NORMAL   (0 << ES8388_PDNADCBIASGEN_SHIFT)
+#define ES8388_PDNADCBIASGEN_LP       (1 << ES8388_PDNADCBIASGEN_SHIFT)
+
+#define ES8388_PDNMICB_SHIFT          (3)
+#define ES8388_PDNMICB_BITMASK        (0x01 << ES8388_PDNMICB_SHIFT)
+#define ES8388_PDNMICB_PWRON          (0 << ES8388_PDNMICB_SHIFT)
+#define ES8388_PDNMICB_PWRDN          (1 << ES8388_PDNMICB_SHIFT)
+
+#define ES8388_PDNADCR_SHIFT          (4)
+#define ES8388_PDNADCR_BITMASK        (0x01 << ES8388_PDNADCR_SHIFT)
+#define ES8388_PDNADCR_PWRUP          (0 << ES8388_PDNADCR_SHIFT)
+#define ES8388_PDNADCR_PWRDN          (1 << ES8388_PDNADCR_SHIFT)
+
+#define ES8388_PDNADCL_SHIFT          (5)
+#define ES8388_PDNADCL_BITMASK        (0x01 << ES8388_PDNADCL_SHIFT)
+#define ES8388_PDNADCL_PWRUP          (0 << ES8388_PDNADCL_SHIFT)
+#define ES8388_PDNADCL_PWRDN          (1 << ES8388_PDNADCL_SHIFT)
+
+#define ES8388_PDNAINR_SHIFT          (6)
+#define ES8388_PDNAINR_BITMASK        (0x01 << ES8388_PDNAINR_SHIFT)
+#define ES8388_PDNAINR_NORMAL         (0 << ES8388_PDNAINR_SHIFT)
+#define ES8388_PDNAINR_PWRDN          (1 << ES8388_PDNAINR_SHIFT)
+
+#define ES8388_PDNAINL_SHIFT          (7)
+#define ES8388_PDNAINL_BITMASK        (0x01 << ES8388_PDNAINL_SHIFT)
+#define ES8388_PDNAINL_NORMAL         (0 << ES8388_PDNAINL_SHIFT)
+#define ES8388_PDNAINL_PWRDN          (1 << ES8388_PDNAINL_SHIFT)
+
+/* 0x04 DAC Power Management */
+
+#define ES8388_ROUT2_SHIFT            (2)
+#define ES8388_ROUT2_BITMASK          (0x01 << ES8388_ROUT2_SHIFT)
+#define ES8388_ROUT2_DISABLE          (0 << ES8388_ROUT2_SHIFT)
+#define ES8388_ROUT2_ENABLE           (1 << ES8388_ROUT2_SHIFT)
+
+#define ES8388_LOUT2_SHIFT            (3)
+#define ES8388_LOUT2_BITMASK          (0x01 << ES8388_LOUT2_SHIFT)
+#define ES8388_LOUT2_DISABLE          (0 << ES8388_LOUT2_SHIFT)
+#define ES8388_LOUT2_ENABLE           (1 << ES8388_LOUT2_SHIFT)
+
+#define ES8388_ROUT1_SHIFT            (4)
+#define ES8388_ROUT1_BITMASK          (0x01 << ES8388_ROUT1_SHIFT)
+#define ES8388_ROUT1_DISABLE          (0 << ES8388_ROUT1_SHIFT)
+#define ES8388_ROUT1_ENABLE           (1 << ES8388_ROUT1_SHIFT)
+
+#define ES8388_LOUT1_SHIFT            (5)
+#define ES8388_LOUT1_BITMASK          (0x01 << ES8388_LOUT1_SHIFT)
+#define ES8388_LOUT1_DISABLE          (0 << ES8388_LOUT1_SHIFT)
+#define ES8388_LOUT1_ENABLE           (1 << ES8388_LOUT1_SHIFT)
+
+#define ES8388_PDNDACR_SHIFT          (6)
+#define ES8388_PDNDACR_BITMASK        (0x01 << ES8388_PDNDACR_SHIFT)
+#define ES8388_PDNDACR_PWRUP          (0 << ES8388_PDNDACR_SHIFT)
+#define ES8388_PDNDACR_PWRDN          (1 << ES8388_PDNDACR_SHIFT)
+
+#define ES8388_PDNDACL_SHIFT          (7)
+#define ES8388_PDNDACL_BITMASK        (0x01 << ES8388_PDNDACL_SHIFT)
+#define ES8388_PDNDACL_PWRUP          (0 << ES8388_PDNDACL_SHIFT)
+#define ES8388_PDNDACL_PWRDN          (1 << ES8388_PDNDACL_SHIFT)
+
+/* 0x05 Chip Low Power 1 */
+
+#define ES8388_LPLOUT2_SHIFT          (3)
+#define ES8388_LPLOUT2_BITMASK        (0x01 << ES8388_LPLOUT2_SHIFT)
+#define ES8388_LPLOUT2_NORMAL         (0 << ES8388_LPLOUT2_SHIFT)
+#define ES8388_LPLOUT2_LP             (1 << ES8388_LPLOUT2_SHIFT)
+
+#define ES8388_LPLOUT1_SHIFT          (5)
+#define ES8388_LPLOUT1_BITMASK        (0x01 << ES8388_LPLOUT1_SHIFT)
+#define ES8388_LPLOUT1_NORMAL         (0 << ES8388_LPLOUT1_SHIFT)
+#define ES8388_LPLOUT1_LP             (1 << ES8388_LPLOUT1_SHIFT)
+
+#define ES8388_LPDACR_SHIFT           (6)
+#define ES8388_LPDACR_BITMASK         (0x01 << ES8388_LPDACR_SHIFT)
+#define ES8388_LPDACR_NORMAL          (0 << ES8388_LPDACR_SHIFT)
+#define ES8388_LPDACR_LP              (1 << ES8388_LPDACR_SHIFT)
+
+#define ES8388_LPDACL_SHIFT           (7)
+#define ES8388_LPDACL_BITMASK         (0x01 << ES8388_LPDACL_SHIFT)
+#define ES8388_LPDACL_NORMAL          (0 << ES8388_LPDACL_SHIFT)
+#define ES8388_LPDACL_LP              (1 << ES8388_LPDACL_SHIFT)
+
+/* 0x06 Chip Low Power 2 */
+
+#define ES8388_LPDACVRP_SHIFT         (0)
+#define ES8388_LPDACVRP_BITMASK       (0x01 << ES8388_LPDACVRP_SHIFT)
+#define ES8388_LPDACVRP_NORMAL        (0 << ES8388_LPDACVRP_SHIFT)
+#define ES8388_LPDACVRP_LP            (1 << ES8388_LPDACVRP_SHIFT)
+
+#define ES8388_LPADCVRP_SHIFT         (1)
+#define ES8388_LPADCVRP_BITMASK       (0x01 << ES8388_LPDACVRP_SHIFT)
+#define ES8388_LPADCVRP_NORMAL        (0 << ES8388_LPDACVRP_SHIFT)
+#define ES8388_LPADCVRP_LP            (1 << ES8388_LPDACVRP_SHIFT)
+
+#define ES8388_LPLMIX_SHIFT           (6)
+#define ES8388_LPLMIX_BITMASK         (0x01 << ES8388_LPLMIX_SHIFT)
+#define ES8388_LPLMIX_NORMAL          (0 << ES8388_LPLMIX_SHIFT)
+#define ES8388_LPLMIX_LP              (1 << ES8388_LPLMIX_SHIFT)
+
+#define ES8388_LPPGA_SHIFT            (7)
+#define ES8388_LPPGA_BITMASK          (0x01 << ES8388_LPPGA_SHIFT)
+#define ES8388_LPPGA_NORMAL           (0 << ES8388_LPPGA_SHIFT)
+#define ES8388_LPPGA_LP               (1 << ES8388_LPPGA_SHIFT)
+
+/* 0x08 Master Mode Control */
+
+#define ES8388_BCLKDIV_SHIFT          (0)
+#define ES8388_BCLKDIV_BITMASK        (0x1f << ES8388_BCLKDIV_SHIFT)
+#define ES8388_BCLKDIV(a)             (a << ES8388_BCLKDIV_SHIFT)
+
+#define ES8388_BCLK_INV_SHIFT         (5)
+#define ES8388_BCLK_INV_BITMASL       (0x01 << ES8388_BCLK_INV_SHIFT)
+#define ES8388_BCLK_INV_NORMAL        (0 << ES8388_BCLK_INV_SHIFT)
+#define ES8388_BCLK_INV_INVERTED      (1 << ES8388_BCLK_INV_SHIFT)
+
+#define ES8388_MCLKDIV2_SHIFT         (6)
+#define ES8388_MCLKDIV2_BITMASK       (0x01 << ES8388_MCLKDIV2_SHIFT)
+#define ES8388_MCLKDIV2_NODIV         (0 << ES8388_MCLKDIV2_SHIFT)
+#define ES8388_MCLKDIV2_DIV2          (1 << ES8388_MCLKDIV2_SHIFT)
+
+#define ES8388_MSC_SHIFT              (7)
+#define ES8388_MSC_BITMASK            (0x01 << ES8388_MSC_SHIFT)
+#define ES8388_MSC_SLAVE              (0 << ES8388_MSC_SHIFT)
+#define ES8388_MSC_MASTER             (1 << ES8388_MSC_SHIFT)
+
+/* 0x09 ADC Control 1 */
+
+#define ES8388_MICAMPR_SHIFT          (0)
+#define ES8388_MICAMPR_BITMASK        (0x0f << ES8388_MICAMPR_SHIFT)
+#define ES8388_MICAMPR(a)             (a << ES8388_MICAMPR_SHIFT)
+
+#define ES8388_MICAMPL_SHIFT          (4)
+#define ES8388_MICAMPL_BITMASK        (0x0f << ES8388_MICAMPL_SHIFT)
+#define ES8388_MICAMPL(a)             (a << ES8388_MICAMPL_SHIFT)
+
+/* 0x0a ADC Control 2 */
+
+#define ES8388_DSR_SHIFT              (2)
+#define ES8388_DSR_BITMASK            (0x01 << ES8388_DSR_SHIFT)
+#define ES8388_DSR_LINPUT1_RINPUT1    (0 << ES8388_DSR_SHIFT)
+#define ES8388_DSR_LINPUT2_RINPUT2    (1 << ES8388_DSR_SHIFT)
+
+#define ES8388_DSSEL_SHIFT            (3)
+#define ES8388_DSSEL_BITMASK          (0x01 << ES8388_DSSEL_SHIFT)
+#define ES8388_DSSEL_ONE_REG          (0 << ES8388_DSSEL_SHIFT)
+#define ES8388_DSSEL_MULT_REG         (1 << ES8388_DSSEL_SHIFT)
+
+#define ES8388_RINSEL_SHIFT           (4)
+#define ES8388_RINSEL_BITMASK         (0x03 << ES8388_RINSEL_SHIFT)
+#define ES8388_RINSEL_RINPUT1         (0 << ES8388_RINSEL_SHIFT)
+#define ES8388_RINSEL_RINPUT2         (1 << ES8388_RINSEL_SHIFT)
+#define ES8388_RINSEL_DIFF            (3 << ES8388_RINSEL_SHIFT)
+
+#define ES8388_LINSEL_SHIFT           (6)
+#define ES8388_LINSEL_BITMASK         (0x03 << ES8388_LINSEL_SHIFT)
+#define ES8388_LINSEL_LINPUT1         (0 << ES8388_LINSEL_SHIFT)
+#define ES8388_LINSEL_LINPUT2         (1 << ES8388_LINSEL_SHIFT)
+#define ES8388_LINSEL_DIFF            (3 << ES8388_LINSEL_SHIFT)
+
+/* 0x0b ADC Control 3 */
+
+#define ES8388_TRI_SHIFT              (2)
+#define ES8388_TRI_BITMASK            (0x01 << ES8388_TRI_SHIFT)
+#define ES8388_TRI_NORMAL             (0 << ES8388_TRI_SHIFT)
+#define ES8388_TRI_TRISTATED          (1 << ES8388_TRI_SHIFT)
+
+#define ES8388_MONOMIX_SHIFT          (3)
+#define ES8388_MONOMIX_BITMASK        (0x03 << ES8388_MONOMIX_SHIFT)
+#define ES8388_MONOMIX_STEREO         (0 << ES8388_MONOMIX_SHIFT)
+#define ES8388_MONOMIX_LADC           (1 << ES8388_MONOMIX_SHIFT)
+#define ES8388_MONOMIX_RADC           (2 << ES8388_MONOMIX_SHIFT)
+
+#define ES8388_DS_SHIFT               (7)
+#define ES8388_DS_BITMASK             (0x01 << ES8388_DS_SHIFT)
+#define ES8388_DS_LINPUT1_RINPUT1     (0 << ES8388_DS_SHIFT)
+#define ES8388_DS_LINPUT2_RINPUT2     (1 << ES8388_DS_SHIFT)
+
+/* 0x0c ADC Control 4 */
+
+#define ES8388_ADCFORMAT_SHIFT        (0)
+#define ES8388_ADCFORMAT_BITMASK      (0x03 << ES8388_ADCFORMAT_SHIFT)
+#define ES8388_ADCFORMAT(a)           (a << ES8388_ADCFORMAT_SHIFT)
+
+#define ES8388_ADCWL_SHIFT            (2)
+#define ES8388_ADCWL_BITMASK          (0x07 << ES8388_ADCWL_SHIFT)
+#define ES8388_ADCWL(a)               (a << ES8388_ADCWL_SHIFT)
+
+#define ES8388_ADCLRP_SHIFT           (5)
+#define ES8388_ADCLRP_BITMASK         (0x01 << ES8388_ADCLRP_SHIFT)
+#define ES8388_ADCLRP_NORM_2ND        (0 << ES8388_ADCLRP_SHIFT)
+#define ES8388_ADCLRP_INV_1ST         (1 << ES8388_ADCLRP_SHIFT)
+
+#define ES8388_DATSEL_SHIFT           (6)
+#define ES8388_DATSEL_BITMASK         (0x03 << ES8388_DATSEL_SHIFT)
+#define ES8388_DATSEL_LL              (0 << ES8388_DATSEL_SHIFT)
+#define ES8388_DATSEL_LR              (1 << ES8388_DATSEL_SHIFT)
+#define ES8388_DATSEL_RR              (2 << ES8388_DATSEL_SHIFT)
+#define ES8388_DATSEL_RL              (3 << ES8388_DATSEL_SHIFT)
+
+/* 0x0d ADC Control 5 */
+
+#define ES8388_ADCFSRATIO_SHIFT       (0)
+#define ES8388_ADCFSRATIO_BITMASK     (0x1f << ES8388_ADCFSRATIO_SHIFT)
+#define ES8388_ADCFSRATIO(a)          (a << ES8388_ADCFSRATIO_SHIFT)
+
+#define ES8388_ADCFSMODE_SHIFT        (5)
+#define ES8388_ADCFSMODE_BITMASK      (0x01 << ES8388_ADCFSMODE_SHIFT)
+#define ES8388_ADCFSMODE_SINGLE       (0 << ES8388_ADCFSMODE_SHIFT)
+#define ES8388_ADCFSMODE_DOUBLE       (1 << ES8388_ADCFSMODE_SHIFT)
+
+/* 0x0e ADC Control 6 */
+
+#define ES8388_ADC_HPF_R_SHIFT        (4)
+#define ES8388_ADC_HPF_R_BITMASK      (0x01 << ES8388_ADC_HPF_R_SHIFT)
+#define ES8388_ADC_HPF_R_DISABLE      (0 << ES8388_ADC_HPF_R_SHIFT)
+#define ES8388_ADC_HPF_R_ENABLE       (1 << ES8388_ADC_HPF_R_SHIFT)
+
+#define ES8388_ADC_HPF_L_SHIFT        (5)
+#define ES8388_ADC_HPF_L_BITMASK      (0x01 << ES8388_ADC_HPF_L_SHIFT)
+#define ES8388_ADC_HPF_L_DISABLE      (0 << ES8388_ADC_HPF_L_SHIFT)
+#define ES8388_ADC_HPF_L_ENABLE       (1 << ES8388_ADC_HPF_L_SHIFT)
+
+#define ES8388_ADC_INVR_SHIFT         (6)
+#define ES8388_ADC_INVR_BITMASK       (0x01 << ES8388_ADC_INVR_SHIFT)
+#define ES8388_ADC_INVR_NORMAL        (0 << ES8388_ADC_INVR_SHIFT)
+#define ES8388_ADC_INVR_INVERTED      (1 << ES8388_ADC_INVR_SHIFT)
+
+#define ES8388_ADC_INVL_SHIFT         (7)
+#define ES8388_ADC_INVL_BITMASK       (0x01 << ES8388_ADC_INVL_SHIFT)
+#define ES8388_ADC_INVL_NORMAL        (0 << ES8388_ADC_INVL_SHIFT)
+#define ES8388_ADC_INVL_INVERTED      (1 << ES8388_ADC_INVL_SHIFT)
+
+/* 0x0f ADC Control 7 */
+
+#define ES8388_ADCMUTE_SHIFT          (2)
+#define ES8388_ADCMUTE_BITMASK        (0x01 << ES8388_ADCMUTE_SHIFT)
+#define ES8388_ADCMUTE(a)             (((int)a) << ES8388_ADCMUTE_SHIFT)
+#define ES8388_ADCMUTE_NORMAL         (0 << ES8388_ADCMUTE_SHIFT)
+#define ES8388_ADCMUTE_MUTED          (1 << ES8388_ADCMUTE_SHIFT)
+
+#define ES8388_ADCLER_SHIFT           (3)
+#define ES8388_ADCLER_BITMASK         (0x01 << ES8388_ADCLER_SHIFT)
+#define ES8388_ADCLER_NORMAL          (0 << ES8388_ADCLER_SHIFT)
+#define ES8388_ADCLER_ADCLEFT         (1 << ES8388_ADCLER_SHIFT)
+
+#define ES8388_ADCSOFTRAMP_SHIFT      (5)
+#define ES8388_ADCSOFTRAMP_BITMASK    (0x01 << ES8388_ADCSOFTRAMP_SHIFT)
+#define ES8388_ADCSOFTRAMP_DISABLE    (0 << ES8388_ADCSOFTRAMP_SHIFT)
+#define ES8388_ADCSOFTRAMP_ENABLE     (1 << ES8388_ADCSOFTRAMP_SHIFT)
+
+#define ES8388_ADCRAMPRATE_SHIFT      (6)
+#define ES8388_ADCRAMPRATE_BITMASK    (0x03 << ES8388_ADCRAMPRATE_SHIFT)
+#define ES8388_ADCRAMPRATE_4LRCK      (0 << ES8388_ADCRAMPRATE_SHIFT)
+#define ES8388_ADCRAMPRATE_8LRCK      (1 << ES8388_ADCRAMPRATE_SHIFT)
+#define ES8388_ADCRAMPRATE_16LRCK     (2 << ES8388_ADCRAMPRATE_SHIFT)
+#define ES8388_ADCRAMPRATE_32LRCK     (3 << ES8388_ADCRAMPRATE_SHIFT)
+
+/* 0x10 ADC Control 8 */
+
+#define ES8388_LADCVOL_SHIFT          (0)
+#define ES8388_LADCVOL_BITMASK        (0xff << ES8388_LADCVOL_SHIFT)
+#define ES8388_LADCVOL(a)             (a << ES8388_LADCVOL_SHIFT)
+
+/* 0x11 ADC Control 9 */
+
+#define ES8388_RADCVOL_SHIFT          (0)
+#define ES8388_RADCVOL_BITMASK        (0xff << ES8388_RADCVOL_SHIFT)
+#define ES8388_RADCVOL(a)             (a << ES8388_RADCVOL_SHIFT)
+
+/* 0x12 ADC Control 10 */
+
+#define ES8388_MINGAIN_SHIFT          (0)
+#define ES8388_MINGAIN_BITMASK        (0x07 << ES8388_MINGAIN_SHIFT)
+#define ES8388_MINGAIN(a)             (a << ES8388_MINGAIN_SHIFT)
+
+#define ES8388_MAXGAIN_SHIFT          (3)
+#define ES8388_MAXGAIN_BITMASK        (0x07 << ES8388_MAXGAIN_SHIFT)
+#define ES8388_MAXGAIN(a)             (a << ES8388_MAXGAIN_SHIFT)
+
+#define ES8388_ADCSEL_SHIFT           (6)
+#define ES8388_ADCSEL_BITMASK         (0x03 << ES8388_ADCSEL_SHIFT)
+#define ES8388_ADCSEL_OFF             (0 << ES8388_ADCSEL_SHIFT)
+#define ES8388_ADCSEL_RIGHT           (1 << ES8388_ADCSEL_SHIFT)
+#define ES8388_ADCSEL_LEFT            (2 << ES8388_ADCSEL_SHIFT)
+#define ES8388_ADCSEL_STEREO          (3 << ES8388_ADCSEL_SHIFT)
+
+/* 0x13 ADC Control 11 */
+
+#define ES8388_ALCHLD_SHIFT           (0)
+#define ES8388_ALCHLD_BITMASK         (0x0f << ES8388_ALCHLD_SHIFT)
+#define ES8388_ALCHLD(a)              (a << ES8388_ALCHLD_SHIFT)
+
+#define ES8388_ALCLVL_SHIFT           (4)
+#define ES8388_ALCLVL_BITMASK         (0x0f << ES8388_ALCLVL_SHIFT)
+#define ES8388_ALCLVL(a)              (a << ES8388_ALCLVL_SHIFT)
+
+/* 0x14 ADC Control 12 */
+
+#define ES8388_ALCATK_SHIFT           (0)
+#define ES8388_ALCATK_BITMASK         (0x0f << ES8388_ALCATK_SHIFT)
+#define ES8388_ALCATK(a)              (a << ES8388_ALCATK_SHIFT)
+
+#define ES8388_ALCDCY_SHIFT           (4)
+#define ES8388_ALCDCY_BITMASK         (0x0f << ES8388_ALCDCY_SHIFT)
+#define ES8388_ALCDCY(a)              (a << ES8388_ALCDCY_SHIFT)
+
+/* 0x15 ADC Control 13 */
+
+#define ES8388_WIN_SIZE_SHIFT         (0)
+#define ES8388_WIN_SIZE_BITMASK       (0x1f << ES8388_WIN_SIZE_SHIFT)
+#define ES8388_WIN_SIZE(a)            (a << ES8388_WIN_SIZE_SHIFT)
+
+#define ES8388_TIME_OUT_SHIFT         (5)
+#define ES8388_TIME_OUT_BITMASK       (0x01 << ES8388_TIME_OUT_SHIFT)
+#define ES8388_TIME_OUT_DISABLE       (0 << ES8388_TIME_OUT_SHIFT)
+#define ES8388_TIME_OUT_ENABLE        (1 << ES8388_TIME_OUT_SHIFT)
+
+#define ES8388_ALCZC_SHIFT            (6)
+#define ES8388_ALCZC_BITMASK          (0x01 << ES8388_ALCZC_SHIFT)
+#define ES8388_ALCZC_DISABLE          (0 << ES8388_ALCZC_SHIFT)
+#define ES8388_ALCZC_ENABLE           (1 << ES8388_ALCZC_SHIFT)
+
+#define ES8388_ALCMODE_SHIFT          (7)
+#define ES8388_ALCMODE_BITMASK        (0x01 << ES8388_ALCMODE_SHIFT)
+#define ES8388_ALCMODE_NORMAL         (0 << ES8388_ALCMODE_SHIFT)
+#define ES8388_ALCMODE_LIMITER        (1 << ES8388_ALCMODE_SHIFT)
+
+/* 0x16 ADC Control 14 */
+
+#define ES8388_NGAT_SHIFT             (0)
+#define ES8388_NGAT_BITMASK           (0x01 << ES8388_NGAT_SHIFT)
+#define ES8388_NGAT_DISABLE           (0 << ES8388_NGAT_SHIFT)
+#define ES8388_NGAT_ENABLE            (1 << ES8388_NGAT_SHIFT)
+
+#define ES8388_NGG_SHIFT              (1)
+#define ES8388_NGG_BITMASK            (0x01 << ES8388_NGG_SHIFT)
+#define ES8388_NGG_CONST              (0 << ES8388_NGG_SHIFT)
+#define ES8388_NGG_MUTE               (1 << ES8388_NGG_SHIFT)
+
+#define ES8388_NGTH_SHIFT             (3)
+#define ES8388_NGTH_BITMASK           (0x1f << ES8388_NGTH_SHIFT)
+#define ES8388_NGTH(a)                (a << ES8388_NGTH_SHIFT)
+
+/* 0x17 DAC Control 1 */
+
+#define ES8388_DACFORMAT_SHIFT        (1)
+#define ES8388_DACFORMAT_BITMASK      (0x03 << ES8388_DACFORMAT_SHIFT)
+#define ES8388_DACFORMAT(a)           (a << ES8388_DACFORMAT_SHIFT)
+
+#define ES8388_DACWL_SHIFT            (3)
+#define ES8388_DACWL_BITMASK          (0x07 << ES8388_DACWL_SHIFT)
+#define ES8388_DACWL(a)               (a << ES8388_DACWL_SHIFT)
+
+#define ES8388_DACLRP_SHIFT           (6)
+#define ES8388_DACLRP_BITMASK         (0x01 << ES8388_DACLRP_SHIFT)
+#define ES8388_DACLRP_NORM_2ND        (0 << ES8388_DACLRP_SHIFT)
+#define ES8388_DACLRP_INV_1ST         (1 << ES8388_DACLRP_SHIFT)
+
+#define ES8388_DACLRSWAP_SHIFT        (7)
+#define ES8388_DACLRSWAP_BITMASK      (0x01 << ES8388_DACLRSWAP_SHIFT)
+#define ES8388_DACLRSWAP_NORMAL       (0 << ES8388_DACLRSWAP_SHIFT)
+#define ES8388_DACLRSWAP_SWAP         (1 << ES8388_DACLRSWAP_SHIFT)
+
+/* 0x18 DAC Control 2 */
+
+#define ES8388_DACFSRATIO_SHIFT       (0)
+#define ES8388_DACFSRATIO_BITMASK     (0x1f << ES8388_DACFSRATIO_SHIFT)
+#define ES8388_DACFSRATIO(a)          (a << ES8388_DACFSRATIO_SHIFT)
+
+#define ES8388_DACFSMODE_SHIFT        (5)
+#define ES8388_DACFSMODE_BITMASK      (0x01 << ES8388_DACFSMODE_SHIFT)
+#define ES8388_DACFSMODE_SINGLE       (0 << ES8388_DACFSMODE_SHIFT)
+#define ES8388_DACFSMODE_DOUBLE       (1 << ES8388_DACFSMODE_SHIFT)
+
+/* 0x19 DAC Control 3 */
+
+#define ES8388_DACMUTE_SHIFT          (2)
+#define ES8388_DACMUTE_BITMASK        (0x01 << ES8388_DACMUTE_SHIFT)
+#define ES8388_DACMUTE(a)             (((int)a) << ES8388_DACMUTE_SHIFT)
+#define ES8388_DACMUTE_NORMAL         (0 << ES8388_DACMUTE_SHIFT)
+#define ES8388_DACMUTE_MUTED          (1 << ES8388_DACMUTE_SHIFT)
+
+#define ES8388_DACLER_SHIFT           (3)
+#define ES8388_DACLER_BITMASK         (0x01 << ES8388_DACLER_SHIFT)
+#define ES8388_DACLER_NORMAL          (0 << ES8388_DACLER_SHIFT)
+#define ES8388_DACLER_ADCLEFT         (1 << ES8388_DACLER_SHIFT)
+
+#define ES8388_DACSOFTRAMP_SHIFT      (5)
+#define ES8388_DACSOFTRAMP_BITMASK    (0x01 << ES8388_DACSOFTRAMP_SHIFT)
+#define ES8388_DACSOFTRAMP_DISABLE    (0 << ES8388_DACSOFTRAMP_SHIFT)
+#define ES8388_DACSOFTRAMP_ENABLE     (1 << ES8388_DACSOFTRAMP_SHIFT)
+
+#define ES8388_DACRAMPRATE_SHIFT      (6)
+#define ES8388_DACRAMPRATE_BITMASK    (0x03 << ES8388_DACRAMPRATE_SHIFT)
+#define ES8388_DACRAMPRATE_4LRCK      (0 << ES8388_DACRAMPRATE_SHIFT)
+#define ES8388_DACRAMPRATE_32LRCK     (1 << ES8388_DACRAMPRATE_SHIFT)
+#define ES8388_DACRAMPRATE_64LRCK     (2 << ES8388_DACRAMPRATE_SHIFT)
+#define ES8388_DACRAMPRATE_128LRCK    (3 << ES8388_DACRAMPRATE_SHIFT)
+
+/* 0x1a DAC Control 4 */
+
+#define ES8388_LDACVOL_SHIFT          (0)
+#define ES8388_LDACVOL_BITMASK        (0xff << ES8388_LDACVOL_SHIFT)
+#define ES8388_LDACVOL(a)             (a << ES8388_LDACVOL_SHIFT)
+
+/* 0x1b DAC Control 5 */
+
+#define ES8388_RDACVOL_SHIFT          (0)
+#define ES8388_RDACVOL_BITMASK        (0xff << ES8388_RDACVOL_SHIFT)
+#define ES8388_RDACVOL(a)             (a << ES8388_RDACVOL_SHIFT)
+
+/* 0x1c DAC Control 6 */
+
+#define ES8388_CLICKFREE_SHIFT        (3)
+#define ES8388_CLICKFREE_BITMASK      (0x01 << ES8388_CLICKFREE_SHIFT)
+#define ES8388_CLICKFREE_DISABLE      (0 << ES8388_CLICKFREE_SHIFT)
+#define ES8388_CLICKFREE_ENABLE       (1 << ES8388_CLICKFREE_SHIFT)
+
+#define ES8388_DAC_INVR_SHIFT         (4)
+#define ES8388_DAC_INVR_BITMASK       (0x01 << ES8388_DAC_INVR_SHIFT)
+#define ES8388_DAC_INVR_NOINV         (0 << ES8388_DAC_INVR_SHIFT)
+#define ES8388_DAC_INVR_180INV        (1 << ES8388_DAC_INVR_SHIFT)
+
+#define ES8388_DAC_INVL_SHIFT         (5)
+#define ES8388_DAC_INVL_BITMASK       (0x01 << ES8388_DAC_INVL_SHIFT)
+#define ES8388_DAC_INVL_NOINV         (0 << ES8388_DAC_INVL_SHIFT)
+#define ES8388_DAC_INVL_180INV        (1 << ES8388_DAC_INVL_SHIFT)
+
+#define ES8388_DEEMP_SHIFT            (6)
+#define ES8388_DEEMP_BITMASK          (0x03 << ES8388_DEEMP_SHIFT)
+#define ES8388_DEEMP_DISABLE          (0 << ES8388_DEEMP_SHIFT)
+#define ES8388_DEEMP_32KHZ            (1 << ES8388_DEEMP_SHIFT)
+#define ES8388_DEEMP_44KHZ            (2 << ES8388_DEEMP_SHIFT)
+#define ES8388_DEEMP_48KHZ            (3 << ES8388_DEEMP_SHIFT)
+
+/* 0x1d DAC Control 7 */
+
+#define ES8388_VPP_SCALE_SHIFT        (0)
+#define ES8388_VPP_SCALE_BITMASK      (0x03 << ES8388_VPP_SCALE_SHIFT)
+#define ES8388_VPP_SCALE_3_5V         (0 << ES8388_VPP_SCALE_SHIFT)
+#define ES8388_VPP_SCALE_4_0V         (1 << ES8388_VPP_SCALE_SHIFT)
+#define ES8388_VPP_SCALE_3_0V         (2 << ES8388_VPP_SCALE_SHIFT)
+#define ES8388_VPP_SCALE_2_5V         (3 << ES8388_VPP_SCALE_SHIFT)
+
+#define ES8388_SE_SHIFT               (2)
+#define ES8388_SE_BITMASK             (0x07 << ES8388_SE_SHIFT)
+#define ES8388_SE(a)                  (a << ES8388_SE_SHIFT)
+
+#define ES8388_MONO_SHIFT             (5)
+#define ES8388_MONO_BITMASK           (0x01 << ES8388_MONO_SHIFT)
+#define ES8388_MONO_STEREO            (0 << ES8388_MONO_SHIFT)
+#define ES8388_MONO_MONO              (1 << ES8388_MONO_SHIFT)
+
+#define ES8388_ZEROR_SHIFT            (6)
+#define ES8388_ZEROR_BITMASK          (0x01 << ES8388_ZEROR_SHIFT)
+#define ES8388_ZEROR_NORMAL           (0 << ES8388_ZEROR_SHIFT)
+#define ES8388_ZEROR_ZERO             (1 << ES8388_ZEROR_SHIFT)
+
+#define ES8388_ZEROL_SHIFT            (7)
+#define ES8388_ZEROL_BITMASK          (0x01 << ES8388_ZEROL_SHIFT)
+#define ES8388_ZEROL_NORMAL           (0 << ES8388_ZEROL_SHIFT)
+#define ES8388_ZEROL_ZERO             (1 << ES8388_ZEROL_SHIFT)
+
+/* 0x1e DAC Control 8
+ * 0x1f DAC Control 9
+ * 0x20 DAC Control 10
+ * 0x21 DAC Control 11
+ * 0x22 DAC Control 12
+ * 0x23 DAC Control 13
+ * 0x24 DAC Control 14
+ * 0x25 DAC Control 15
+ */
+
+#define ES8388_SHELVING_COEF_SHIFT    (0)
+#define ES8388_SHELVING_COEF_BITMASK  (0xff << ES8388_SHELVING_COEF_SHIFT)
+#define ES8388_SHELVING_COEF(a)       (a << ES8388_SHELVING_COEF_SHIFT)
+
+/* 0x26 DAC Control 16 */
+
+#define ES8388_RMIXSEL_SHIFT          (0)
+#define ES8388_RMIXSEL_BITMASK        (0x07 << ES8388_RMIXSEL_SHIFT)
+#define ES8388_RMIXSEL_RIN1           (0 << ES8388_RMIXSEL_SHIFT)
+#define ES8388_RMIXSEL_RIN2           (1 << ES8388_RMIXSEL_SHIFT)
+#define ES8388_RMIXSEL_PIN            (3 << ES8388_RMIXSEL_SHIFT)
+#define ES8388_RMIXSEL_NIN            (4 << ES8388_RMIXSEL_SHIFT)
+
+#define ES8388_LMIXSEL_SHIFT          (3)
+#define ES8388_LMIXSEL_BITMASK        (0x07 << ES8388_LMIXSEL_SHIFT)
+#define ES8388_LMIXSEL_LIN1           (0 << ES8388_LMIXSEL_SHIFT)
+#define ES8388_LMIXSEL_LIN2           (1 << ES8388_LMIXSEL_SHIFT)
+#define ES8388_LMIXSEL_PIN            (3 << ES8388_LMIXSEL_SHIFT)
+#define ES8388_LMIXSEL_NIN            (4 << ES8388_LMIXSEL_SHIFT)
+
+/* 0x27 DAC Control 17 */
+
+#define ES8388_LI2LOVOL_SHIFT         (3)
+#define ES8388_LI2LOVOL_BITMASK       (0x07 << ES8388_LI2LOVOL_SHIFT)
+#define ES8388_LI2LOVOL(a)            (a << ES8388_LI2LOVOL_SHIFT)
+
+#define ES8388_LI2LO_SHIFT            (6)
+#define ES8388_LI2LO_BITMASK          (0x01 << ES8388_LI2LO_SHIFT)
+#define ES8388_LI2LO_DISABLE          (0 << ES8388_LI2LO_SHIFT)
+#define ES8388_LI2LO_ENABLE           (1 << ES8388_LI2LO_SHIFT)
+
+#define ES8388_LD2LO_SHIFT            (7)
+#define ES8388_LD2LO_BITMASK          (0x01 << ES8388_LD2LO_SHIFT)
+#define ES8388_LD2LO_DISABLE          (0 << ES8388_LD2LO_SHIFT)
+#define ES8388_LD2LO_ENABLE           (1 << ES8388_LD2LO_SHIFT)
+
+/* 0x2a DAC Control 20 */
+
+#define ES8388_RI2ROVOL_SHIFT         (3)
+#define ES8388_RI2ROVOL_BITMASK       (0x07 << ES8388_RI2ROVOL_SHIFT)
+#define ES8388_RI2ROVOL(a)            (a << ES8388_RI2ROVOL_SHIFT)
+
+#define ES8388_RI2RO_SHIFT            (6)
+#define ES8388_RI2RO_BITMASK          (0x01 << ES8388_RI2RO_SHIFT)
+#define ES8388_RI2RO_DISABLE          (0 << ES8388_RI2RO_SHIFT)
+#define ES8388_RI2RO_ENABLE           (1 << ES8388_RI2RO_SHIFT)
+
+#define ES8388_RD2RO_SHIFT            (7)
+#define ES8388_RD2RO_BITMASK          (0x01 << ES8388_RD2RO_SHIFT)
+#define ES8388_RD2RO_DISABLE          (0 << ES8388_RD2RO_SHIFT)
+#define ES8388_RD2RO_ENABLE           (1 << ES8388_RD2RO_SHIFT)
+
+/* 0x2b DAC Control 21 */
+
+#define ES8388_DAC_DLL_PWD_SHIFT      (2)
+#define ES8388_DAC_DLL_PWD_BITMASK    (0x01 << ES8388_DAC_DLL_PWD_SHIFT)
+#define ES8388_DAC_DLL_PWD_NORMAL     (0 << ES8388_DAC_DLL_PWD_SHIFT)
+#define ES8388_DAC_DLL_PWD_PWRDN      (1 << ES8388_DAC_DLL_PWD_SHIFT)
+
+#define ES8388_ADC_DLL_PWD_SHIFT      (3)
+#define ES8388_ADC_DLL_PWD_BITMASK    (0x01 << ES8388_ADC_DLL_PWD_SHIFT)
+#define ES8388_ADC_DLL_PWD_NORMAL     (0 << ES8388_ADC_DLL_PWD_SHIFT)
+#define ES8388_ADC_DLL_PWD_PWRDN      (1 << ES8388_ADC_DLL_PWD_SHIFT)
+
+#define ES8388_MCLK_DIS_SHIFT         (4)
+#define ES8388_MCLK_DIS_BITMASK       (0x01 << ES8388_MCLK_DIS_SHIFT)
+#define ES8388_MCLK_DIS_NORMAL        (0 << ES8388_MCLK_DIS_SHIFT)
+#define ES8388_MCLK_DIS_DISABLE       (1 << ES8388_MCLK_DIS_SHIFT)
+
+#define ES8388_OFFSET_DIS_SHIFT       (5)
+#define ES8388_OFFSET_DIS_BITMASK     (0x01 << ES8388_OFFSET_DIS_SHIFT)
+#define ES8388_OFFSET_DIS_DISABLE     (0 << ES8388_OFFSET_DIS_SHIFT)
+#define ES8388_OFFSET_DIS_ENABLE      (1 << ES8388_OFFSET_DIS_SHIFT)
+
+#define ES8388_LRCK_SEL_SHIFT         (6)
+#define ES8388_LRCK_SEL_BITMASK       (0x01 << ES8388_LRCK_SEL_SHIFT)
+#define ES8388_LRCK_SEL_DAC           (0 << ES8388_LRCK_SEL_SHIFT)
+#define ES8388_LRCK_SEL_ADC           (1 << ES8388_LRCK_SEL_SHIFT)
+
+#define ES8388_SLRCK_SHIFT            (7)
+#define ES8388_SLRCK_BITMASK          (0x01 << ES8388_SLRCK_SHIFT)
+#define ES8388_SLRCK_SEPARATE         (0 << ES8388_SLRCK_SHIFT)
+#define ES8388_SLRCK_SAME             (1 << ES8388_SLRCK_SHIFT)
+
+/* 0x2c DAC Control 22 */
+
+#define ES8388_OFFSET_SHIFT           (0)
+#define ES8388_OFFSET_BITMASK         (0xff << ES8388_OFFSET_SHIFT)
+#define ES8388_OFFSET(a)              (a << ES8388_OFFSET_SHIFT)
+
+/* 0x2d DAC Control 23 */
+
+#define ES8388_VROI_SHIFT             (4)
+#define ES8388_VROI_BITMASK           (0x01 << ES8388_VROI_SHIFT)
+#define ES8388_VROI_1_5K              (0 << ES8388_VROI_SHIFT)
+#define ES8388_VROI_40K               (1 << ES8388_VROI_SHIFT)
+
+/* 0x2e DAC Control 24 */
+
+#define ES8388_LOUT1VOL_SHIFT         (0)
+#define ES8388_LOUT1VOL_BITMASK       (0x3f << ES8388_LOUT1VOL_SHIFT)
+#define ES8388_LOUT1VOL(a)            (a << ES8388_LOUT1VOL_SHIFT)
+
+/* 0x2f DAC Control 25 */
+
+#define ES8388_ROUT1VOL_SHIFT         (0)
+#define ES8388_ROUT1VOL_BITMASK       (0x3f << ES8388_ROUT1VOL_SHIFT)
+#define ES8388_ROUT1VOL(a)            (a << ES8388_ROUT1VOL_SHIFT)
+
+/* 0x30 DAC Control 26 */
+
+#define ES8388_LOUT2VOL_SHIFT         (0)
+#define ES8388_LOUT2VOL_BITMASK       (0x3f << ES8388_LOUT2VOL_SHIFT)
+#define ES8388_LOUT2VOL(a)            (a << ES8388_LOUT2VOL_SHIFT)
+
+/* 0x31 DAC Control 27 */
+
+#define ES8388_ROUT2VOL_SHIFT         (0)
+#define ES8388_ROUT2VOL_BITMASK       (0x3f << ES8388_ROUT2VOL_SHIFT)
+#define ES8388_ROUT2VOL(a)            (a << ES8388_ROUT2VOL_SHIFT)
+
+/* Codec Default Parameters *************************************************/
+
+#define ES8388_DEFAULT_SAMPRATE      44100
+#define ES8388_DEFAULT_NCHANNELS     2
+#define ES8388_DEFAULT_BPSAMP        16
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+typedef enum
+{
+  ES8388_DAC_OUTPUT_LINE1,
+  ES8388_DAC_OUTPUT_LINE2,
+  ES8388_DAC_OUTPUT_ALL,
+} es8388_dac_output_t;
+
+typedef enum
+{
+  ES8388_ADC_INPUT_LINE1,
+  ES8388_ADC_INPUT_LINE2,
+  ES8388_ADC_INPUT_ALL,
+  ES8388_ADC_INPUT_DIFFERENCE,
+} es8388_adc_input_t;
+
+typedef enum
+{
+  ES8388_WORD_LENGTH_16BITS = 0x03,
+  ES8388_WORD_LENGTH_18BITS = 0x02,
+  ES8388_WORD_LENGTH_20BITS = 0x01,
+  ES8388_WORD_LENGTH_24BITS = 0x00,
+  ES8388_WORD_LENGTH_32BITS = 0x04,
+} es8388_word_length_t;
+
+typedef enum
+{
+  ES8388_MCLK_DIV_AUTO,
+  ES8388_MCLK_DIV_1,
+  ES8388_MCLK_DIV_2,
+  ES8388_MCLK_DIV_3,
+  ES8388_MCLK_DIV_4,
+  ES8388_MCLK_DIV_6,
+  ES8388_MCLK_DIV_8,
+  ES8388_MCLK_DIV_9,
+  ES8388_MCLK_DIV_11,
+  ES8388_MCLK_DIV_12,
+  ES8388_MCLK_DIV_16,
+  ES8388_MCLK_DIV_18,
+  ES8388_MCLK_DIV_22,
+  ES8388_MCLK_DIV_24,
+  ES8388_MCLK_DIV_33,
+  ES8388_MCLK_DIV_36,
+  ES8388_MCLK_DIV_44,
+  ES8388_MCLK_DIV_48,
+  ES8388_MCLK_DIV_66,
+  ES8388_MCLK_DIV_72,
+  ES8388_MCLK_DIV_5,
+  ES8388_MCLK_DIV_10,
+  ES8388_MCLK_DIV_15,
+  ES8388_MCLK_DIV_17,
+  ES8388_MCLK_DIV_20,
+  ES8388_MCLK_DIV_25,
+  ES8388_MCLK_DIV_30,
+  ES8388_MCLK_DIV_32,
+  ES8388_MCLK_DIV_34,
+  ES8388_MCLK_DIV_7,
+  ES8388_MCLK_DIV_13,
+  ES8388_MCLK_DIV_14,
+} es8388_sclk_div_t;
+
+typedef enum
+{
+  ES8388_LCLK_DIV_128 = 0,
+  ES8388_LCLK_DIV_192 = 1,
+  ES8388_LCLK_DIV_256 = 2,
+  ES8388_LCLK_DIV_384 = 3,
+  ES8388_LCLK_DIV_512 = 4,
+  ES8388_LCLK_DIV_576 = 5,
+  ES8388_LCLK_DIV_768 = 6,
+  ES8388_LCLK_DIV_1024 = 7,
+  ES8388_LCLK_DIV_1152 = 8,
+  ES8388_LCLK_DIV_1408 = 9,
+  ES8388_LCLK_DIV_1536 = 10,
+  ES8388_LCLK_DIV_2112 = 11,
+  ES8388_LCLK_DIV_2304 = 12,
+  ES8388_LCLK_DIV_125 = 16,
+  ES8388_LCLK_DIV_136 = 17,
+  ES8388_LCLK_DIV_250 = 18,
+  ES8388_LCLK_DIV_272 = 19,
+  ES8388_LCLK_DIV_375 = 20,
+  ES8388_LCLK_DIV_500 = 21,
+  ES8388_LCLK_DIV_544 = 22,
+  ES8388_LCLK_DIV_750 = 23,
+  ES8388_LCLK_DIV_1000 = 24,
+  ES8388_LCLK_DIV_1088 = 25,
+  ES8388_LCLK_DIV_1496 = 26,
+  ES8388_LCLK_DIV_1500 = 27,
+} es8388_lclk_div_t;
+
+typedef enum
+{
+  ES8388_D2SE_PGA_GAIN_DIS,
+  ES8388_D2SE_PGA_GAIN_EN
+} es8388_d2se_pga_t;
+
+typedef enum
+{
+  ES8388_ADC_CHANNEL_LINPUT1_RINPUT1 = 0x00,
+  ES8388_ADC_CHANNEL_MIC1 = 0x05,
+  ES8388_ADC_CHANNEL_MIC2 = 0x06,
+  ES8388_ADC_CHANNEL_LINPUT2_RINPUT2 = 0x50,
+  ES8388_ADC_CHANNEL_DIFFERENCE = 0xf0,
+} es8388_adc_channel_t;
+
+typedef enum
+{
+  ES8388_DAC_CHANNEL_LOUT1 = 0x04,
+  ES8388_DAC_CHANNEL_LOUT2 = 0x08,
+  ES8388_DAC_CHANNEL_SPK = 0x09,
+  ES8388_DAC_CHANNEL_ROUT1 = 0x10,
+  ES8388_DAC_CHANNEL_ROUT2 = 0x20,
+  ES8388_DAC_CHANNEL_ALL = 0x3c,
+} es8388_dac_channel_t;
+
+typedef enum
+{
+  ES8388_MIC_GAIN_0DB,
+  ES8388_MIC_GAIN_3DB,
+  ES8388_MIC_GAIN_6DB,
+  ES8388_MIC_GAIN_9DB,
+  ES8388_MIC_GAIN_12DB,
+  ES8388_MIC_GAIN_15DB,
+  ES8388_MIC_GAIN_18DB,
+  ES8388_MIC_GAIN_21DB,
+  ES8388_MIC_GAIN_24DB,
+} es8388_mic_gain_t;
+
+typedef enum
+{
+  ES8388_MIXER_GAIN_6DB,
+  ES8388_MIXER_GAIN_3DB,
+  ES8388_MIXER_GAIN_0DB,
+  ES8388_MIXER_GAIN_N3DB,
+  ES8388_MIXER_GAIN_N6DB,
+  ES8388_MIXER_GAIN_N9DB,
+  ES8388_MIXER_GAIN_N12DB,
+  ES8388_MIXER_GAIN_N15DB,
+} es8388_mixer_gain_t;
+
+typedef enum
+{
+  ES8388_MODULE_ADC = 1,
+  ES8388_MODULE_DAC,
+  ES8388_MODULE_ADC_DAC,
+  ES8388_MODULE_LINE,
+} es8388_module_t;
+
+typedef enum
+{
+  ES8388_MODE_SLAVE,
+  ES8388_MODE_MASTER,
+} es8388_mode_t;
+
+typedef enum
+{
+  ES8388_I2S_NORMAL,
+  ES8388_I2S_LEFT,
+  ES8388_I2S_RIGHT,
+  ES8388_I2S_DSP,
+} es8388_i2s_fmt_t;
+
+struct mclk_rate_s
+{
+  uint32_t mclk;
+  uint32_t sample_rate;
+  uint16_t multiple;
+};
+
+struct es8388_dev_s
+{
+  /* We are an audio lower half driver (We are also the upper "half" of
+   * the ES8388 driver with respect to the board lower half driver).
+   *
+   * Terminology:
+   * Our "lower" half audio instances will be called dev for the publicly
+   * visible version and "priv" for the version that only this driver knows
+   * From the point of view of this driver, it is the board lower "half"
+   * that is referred to as "lower".
+   */
+
+  struct audio_lowerhalf_s            dev;              /* ES8388 audio lower half (this device) */
+
+  FAR const struct es8388_lower_s    *lower;            /* Pointer to the board lower functions */
+  FAR struct i2c_master_s            *i2c;              /* I2C driver to use */
+  FAR struct i2s_dev_s               *i2s;              /* I2S driver to use */
+  struct dq_queue_s                   pendq;            /* Queue of pending buffers to be sent */
+  struct dq_queue_s                   doneq;            /* Queue of sent buffers to be returned */
+  struct file                         mq;               /* Message queue for receiving messages */
+  char                                mqname[16];       /* Our message queue name */
+  pthread_t                           threadid;         /* ID of our thread */
+  uint32_t                            bitrate;          /* Actual programmed bit rate */
+  mutex_t                             pendlock;         /* Protect pendq */
+  uint32_t                            samprate;         /* Configured samprate (samples/sec) */
+#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
+#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
+  uint16_t                            balance;          /* Current balance level {0..1000} */
+#endif /* CONFIG_AUDIO_EXCLUDE_BALANCE */
+  uint16_t                            volume;           /* Current volume level {0..1000} */
+#endif /* CONFIG_AUDIO_EXCLUDE_VOLUME */
+  uint8_t                             nchannels;        /* Number of channels (1 or 2) */
+  uint8_t                             bpsamp;           /* Bits per sample */
+  volatile uint8_t                    inflight;         /* Number of audio buffers in-flight */
+  bool                                running;          /* True: Worker thread is running */
+  bool                                paused;           /* True: Playing is paused */
+  bool                                mute;             /* True: Output is muted */
+#ifndef CONFIG_AUDIO_EXCLUDE_STOP
+  bool                                terminating;      /* True: Stop requested */
+#endif
+  bool                                reserved;         /* True: Device is reserved */
+  volatile int                        result;           /* The result of the last transfer */
+  es8388_module_t                     audio_mode;       /* The current audio mode of the ES8388 chip */
+  es8388_dac_output_t                 dac_output;       /* The current output of the ES8388 DAC */
+  es8388_adc_input_t                  adc_input;        /* The current input of the ES8388 ADC */
+  uint32_t                            mclk;             /* The current MCLK frequency */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+static const struct mclk_rate_s es8388_mclk_rate[] =
+{
+  /* 8kHz support is board dependent */
+
+  /* 12.288 MHz */
+
+  {
+    12288000, /* mclk        */
+    12000,    /* sample_rate */
+    1024,     /* multiple    */
+  },
+  {
+    12288000, /* mclk        */
+    16000,    /* sample_rate */
+    768,      /* multiple    */
+  },
+  {
+    12288000, /* mclk        */
+    24000,    /* sample_rate */
+    512,      /* multiple    */
+  },
+  {
+    12288000, /* mclk        */
+    32000,    /* sample_rate */
+    384,      /* multiple    */
+  },
+  {
+    12288000, /* mclk        */
+    48000,    /* sample_rate */
+    256,      /* multiple    */
+  },
+
+  /* 11.2896 MHz */
+
+  {
+    11289600, /* mclk        */
+    11025,    /* sample_rate */
+    1024      /* multiple    */
+  },
+  {
+    11289600, /* mclk        */
+    22050,    /* sample_rate */
+    512       /* multiple    */
+  },
+  {
+    11289600, /* mclk        */
+    44100,    /* sample_rate */
+    256       /* multiple    */
+  },
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: es8388_readreg
+ *
+ * Description:
+ *    Read the specified 8-bit register from the ES8388 device.
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_ES8388_REGDUMP)
+struct es8388_dev_s;
+uint8_t es8388_readreg(FAR struct es8388_dev_s *priv, uint8_t regaddr);
+#endif
+
+#endif /* CONFIG_AUDIO */
+#endif /* __DRIVERS_AUDIO_ES8388_H */
diff --git a/drivers/audio/es8388_debug.c b/drivers/audio/es8388_debug.c
new file mode 100644
index 0000000000..d42d5453d9
--- /dev/null
+++ b/drivers/audio/es8388_debug.c
@@ -0,0 +1,163 @@
+/****************************************************************************
+ * drivers/audio/es8388_debug.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* References:
+ * -  "ES8388 Low Power Stereo Audio CODEC With Headphone Amplifier",
+ *    July 2018, Rev 5.0, Everest Semiconductor
+ * -  The framework for this driver is based on Taras Drozdovsky's CS43L22
+ *    driver.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <assert.h>
+#include <fixedmath.h>
+#include <syslog.h>
+
+#include <nuttx/audio/audio.h>
+#include <nuttx/audio/es8388.h>
+
+#include "es8388.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+#ifdef CONFIG_ES8388_REGDUMP
+struct es8388_regdump_s
+{
+  FAR const char *regname;
+  uint8_t regaddr;
+};
+#endif
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+#ifdef CONFIG_ES8388_REGDUMP
+static const struct es8388_regdump_s g_es8388_debug[] =
+{
+  {"Chip Control 1",            ES8388_CONTROL1      },
+  {"Chip Control 2",            ES8388_CONTROL2      },
+  {"Chip Power Management",     ES8388_CHIPPOWER     },
+  {"ADC Power Management",      ES8388_ADCPOWER      },
+  {"DAC Power Management",      ES8388_DACPOWER      },
+  {"Chip Low Power 1",          ES8388_CHIPLOPOW1    },
+  {"Chip Low Power 2",          ES8388_CHIPLOPOW2    },
+  {"Analog Volume Management",  ES8388_ANAVOLMANAG   },
+  {"Master Mode Control",       ES8388_MASTERMODE    },
+  {"ADC Control 1",             ES8388_ADCCONTROL1   },
+  {"ADC Control 2",             ES8388_ADCCONTROL2   },
+  {"ADC Control 3",             ES8388_ADCCONTROL3   },
+  {"ADC Control 4",             ES8388_ADCCONTROL4   },
+  {"ADC Control 5",             ES8388_ADCCONTROL5   },
+  {"ADC Control 6",             ES8388_ADCCONTROL6   },
+  {"ADC Control 7",             ES8388_ADCCONTROL7   },
+  {"ADC Control 8",             ES8388_ADCCONTROL8   },
+  {"ADC Control 9",             ES8388_ADCCONTROL9   },
+  {"ADC Control 10",            ES8388_ADCCONTROL10  },
+  {"ADC Control 11",            ES8388_ADCCONTROL11  },
+  {"ADC Control 12",            ES8388_ADCCONTROL12  },
+  {"ADC Control 13",            ES8388_ADCCONTROL13  },
+  {"ADC Control 14",            ES8388_ADCCONTROL14  },
+  {"DAC Control 1",             ES8388_DACCONTROL1   },
+  {"DAC Control 2",             ES8388_DACCONTROL2   },
+  {"DAC Control 3",             ES8388_DACCONTROL3   },
+  {"DAC Control 4",             ES8388_DACCONTROL4   },
+  {"DAC Control 5",             ES8388_DACCONTROL5   },
+  {"DAC Control 6",             ES8388_DACCONTROL6   },
+  {"DAC Control 7",             ES8388_DACCONTROL7   },
+  {"DAC Control 8",             ES8388_DACCONTROL8   },
+  {"DAC Control 9",             ES8388_DACCONTROL9   },
+  {"DAC Control 10",            ES8388_DACCONTROL10  },
+  {"DAC Control 11",            ES8388_DACCONTROL11  },
+  {"DAC Control 12",            ES8388_DACCONTROL12  },
+  {"DAC Control 13",            ES8388_DACCONTROL13  },
+  {"DAC Control 14",            ES8388_DACCONTROL14  },
+  {"DAC Control 15",            ES8388_DACCONTROL15  },
+  {"DAC Control 16",            ES8388_DACCONTROL16  },
+  {"DAC Control 17",            ES8388_DACCONTROL17  },
+  {"DAC Control 18",            ES8388_DACCONTROL18  },
+  {"DAC Control 19",            ES8388_DACCONTROL19  },
+  {"DAC Control 20",            ES8388_DACCONTROL20  },
+  {"DAC Control 21",            ES8388_DACCONTROL21  },
+  {"DAC Control 22",            ES8388_DACCONTROL22  },
+  {"DAC Control 23",            ES8388_DACCONTROL23  },
+  {"DAC Control 24",            ES8388_DACCONTROL24  },
+  {"DAC Control 25",            ES8388_DACCONTROL25  },
+  {"DAC Control 26",            ES8388_DACCONTROL26  },
+  {"DAC Control 27",            ES8388_DACCONTROL27  },
+  {"DAC Control 28",            ES8388_DACCONTROL28  },
+  {"DAC Control 29",            ES8388_DACCONTROL29  },
+  {"DAC Control 30",            ES8388_DACCONTROL30  },
+};
+
+#  define ES8388_NREGISTERS (sizeof(g_es8388_debug) / sizeof(struct es8388_regdump_s))
+#endif /* CONFIG_ES8388_REGDUMP */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: es8388_dump_registers
+ *
+ * Description:
+ *   Dump the contents of all ES8388 registers to the syslog device
+ *
+ * Input Parameters:
+ *   dev - The device instance returned by es8388_initialize
+ *   msg - Message to appear before registers
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ES8388_REGDUMP
+void es8388_dump_registers(FAR struct audio_lowerhalf_s *dev,
+                           FAR const char *msg)
+{
+  int i;
+
+  syslog(LOG_INFO, "ES8388 Registers: %s\n", msg);
+  for (i = 0; i < ES8388_NREGISTERS; i++)
+    {
+      syslog(LOG_INFO, "    %s[%02x]: %02x\n",
+             g_es8388_debug[i].regname, g_es8388_debug[i].regaddr,
+             es8388_readreg((FAR struct es8388_dev_s *)dev,
+                            g_es8388_debug[i].regaddr));
+    }
+}
+#endif /* CONFIG_ES8388_REGDUMP */
diff --git a/include/nuttx/audio/audio.h b/include/nuttx/audio/audio.h
index 49f6a6771c..5189ec9a2d 100644
--- a/include/nuttx/audio/audio.h
+++ b/include/nuttx/audio/audio.h
@@ -232,6 +232,31 @@
 #define AUDIO_BIT_RATE_172K         0x40
 #define AUDIO_BIT_RATE_192K         0x80
 
+/* Audio Volume Limits ******************************************************/
+
+/* As nxplayer passes a value in the range (0..1000) to the ioctl, all audio
+ * drivers that implement volume expect a value from 0 to 1000 from the ioctl
+ */
+
+#define AUDIO_VOLUME_MAX            1000
+#define AUDIO_VOLUME_MAX_FLOAT      1000.0f
+#define AUDIO_VOLUME_MIN            0
+#define AUDIO_VOLUME_MIN_FLOAT      0.0f
+
+/* Audio Balance Limits *****************************************************/
+
+/* As nxplayer passes a value in the range (0..1000) to the ioctl, all audio
+ * drivers that implement balance expect a value from 0 to 1000 from the
+ * ioctl
+ */
+
+#define AUDIO_BALANCE_RIGHT          1000
+#define AUDIO_BALANCE_RIGHT_FLOAT    1000.0f
+#define AUDIO_BALANCE_CENTER         500
+#define AUDIO_BALANCE_CENTER_FLOAT   500.0f
+#define AUDIO_BALANCE_LEFT           0
+#define AUDIO_BALANCE_LEFT_FLOAT     0.0f
+
 /* Supported Feature Units controls *****************************************/
 
 #define AUDIO_FU_UNDEF              0x0000
diff --git a/include/nuttx/audio/es8388.h b/include/nuttx/audio/es8388.h
new file mode 100644
index 0000000000..36056a044c
--- /dev/null
+++ b/include/nuttx/audio/es8388.h
@@ -0,0 +1,191 @@
+/****************************************************************************
+ * include/nuttx/audio/es8388.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_AUDIO_ES8388_H
+#define __INCLUDE_NUTTX_AUDIO_ES8388_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <nuttx/irq.h>
+
+#ifdef CONFIG_AUDIO_ES8388
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************
+ *
+ * CONFIG_AUDIO_ES8388 - Enables ES8388 support
+ * CONFIG_ES8388_INITVOLUME - The initial volume level
+ *                             in the range {0..1000}
+ * CONFIG_ES8388_INFLIGHT - Maximum number of buffers that the ES8388
+ *   driver will send to the I2S driver before any have completed.
+ * CONFIG_ES8388_MSG_PRIO - Priority of messages sent to the ES8388
+ *   worker thread.
+ * CONFIG_ES8388_BUFFER_SIZE - Preferred buffer size
+ * CONFIG_ES8388_NUM_BUFFERS - Preferred number of buffers
+ * CONFIG_ES8388_WORKER_STACKSIZE - Stack size to use when creating the the
+ *   ES8388 worker thread.
+ * CONFIG_ES8388_REGDUMP - Enable logic to dump all ES8388 registers to
+ *   the SYSLOG device.
+ */
+
+/* Pre-requisites */
+
+#ifndef CONFIG_AUDIO
+#  error CONFIG_AUDIO is required for audio subsystem support
+#endif
+
+#ifndef CONFIG_I2S
+#  error CONFIG_I2S is required by the ES8388 driver
+#endif
+
+#ifndef CONFIG_I2C
+#  error CONFIG_I2C is required by the ES8388 driver
+#endif
+
+#ifndef CONFIG_SCHED_WORKQUEUE
+#  error CONFIG_SCHED_WORKQUEUE is required by the ES8388 driver
+#endif
+
+/* Default configuration values */
+
+#ifndef CONFIG_ES8388_INPUT_INITVOLUME
+#  define CONFIG_ES8388_INPUT_INITVOLUME       1000
+#endif
+
+#ifndef CONFIG_ES8388_OUTPUT_INITVOLUME
+#  define CONFIG_ES8388_OUTPUT_INITVOLUME      400
+#endif
+
+#ifndef CONFIG_ES8388_INFLIGHT
+#  define CONFIG_ES8388_INFLIGHT               2
+#endif
+
+#if CONFIG_ES8388_INFLIGHT > 255
+#  error CONFIG_ES8388_INFLIGHT must fit in a uint8_t
+#endif
+
+#ifndef CONFIG_ES8388_MSG_PRIO
+#  define CONFIG_ES8388_MSG_PRIO               1
+#endif
+
+#ifndef CONFIG_ES8388_BUFFER_SIZE
+#  define CONFIG_ES8388_BUFFER_SIZE            8192
+#endif
+
+#ifndef CONFIG_ES8388_NUM_BUFFERS
+#  define CONFIG_ES8388_NUM_BUFFERS            4
+#endif
+
+#ifndef CONFIG_ES8388_WORKER_STACKSIZE
+#  define CONFIG_ES8388_WORKER_STACKSIZE       2048
+#endif
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct es8388_lower_s;
+
+struct es8388_lower_s
+{
+  /* I2C characterization */
+
+  uint32_t frequency;  /* Initial I2C frequency */
+  uint8_t  address;    /* 7-bit I2C address (only bits 0-6 used) */
+};
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: es8388_initialize
+ *
+ * Description:
+ *   Initialize the ES8388 device.
+ *
+ * Input Parameters:
+ *   i2c     - An I2C driver instance
+ *   i2s     - An I2S driver instance
+ *   lower   - Persistent board configuration data
+ *
+ * Returned Value:
+ *   A new lower half audio interface for the ES8388 device is returned on
+ *   success; NULL is returned on failure.
+ *
+ ****************************************************************************/
+
+struct i2c_master_s;
+struct i2s_dev_s;
+struct audio_lowerhalf_s;
+
+FAR struct audio_lowerhalf_s *
+  es8388_initialize(FAR struct i2c_master_s *i2c,
+                    FAR struct i2s_dev_s *i2s,
+                    FAR const struct es8388_lower_s *lower);
+
+/****************************************************************************
+ * Name: es8388_dump_registers
+ *
+ * Description:
+ *   Dump the contents of all ES8388 registers to the syslog device
+ *
+ * Input Parameters:
+ *   dev - The device instance returned by es8388_initialize
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_ES8388_REGDUMP
+void es8388_dump_registers(FAR struct audio_lowerhalf_s *dev,
+                           FAR const char *msg);
+#else
+#  define es8388_dump_registers(d,m)
+#endif
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_AUDIO_ES8388 */
+#endif /* __INCLUDE_NUTTX_AUDIO_ES8388_H */