diff --git a/drivers/lcd/pcf8574_lcd_backpack.c b/drivers/lcd/pcf8574_lcd_backpack.c
new file mode 100644
index 0000000000..22fd29bb96
--- /dev/null
+++ b/drivers/lcd/pcf8574_lcd_backpack.c
@@ -0,0 +1,1403 @@
+/****************************************************************************
+ * drivers/lcd/pcf8574_lcd_backpack.c
+ *
+ *   Copyright (C) 2016 Gregory Nutt. All rights reserved.
+ *   Author: dev@ziggurat29.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <string.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/kmalloc.h>
+#include <nuttx/ascii.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/lcd/slcd_codec.h>
+#include <nuttx/lcd/pcf8574_lcd_backpack.h>
+
+#ifndef CONFIG_LIB_SLCDCODEC
+# error please also select Library Routines, Segment LCD CODEC
+#endif
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* The PCF8574 is a 100 KHz device */
+
+#define I2C_FREQ            100000
+
+/* timing characteristics of the LCD interface */
+
+#define DELAY_US_NYBBLE0    200
+#define DELAY_US_NYBBLE1    100
+#define DELAY_US_WRITE      35
+#define DELAY_US_HOMECLEAR  1500
+
+/* HD44780 commands */
+
+#define CMD_CLEAR           0x01
+#define CMD_HOME            0x02
+#define CMD_CURSOR_ON_SOLID 0x0e
+#define CMD_CURSOR_OFF      0x0c
+#define CMD_CURSOR_ON_BLINK 0x0f
+#define CMD_SET_CGADDR      0x40
+#define CMD_SET_DDADDR      0x80
+
+#ifdef CONFIG_DEBUG_LCD
+#  define lcddbg            dbg
+#  define lcdvdbg           vdbg
+#else
+#  define lcddbg(x...)
+#  define lcdvdbg(x...)
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct pcf8574_lcd_dev_s
+{
+  FAR struct i2c_master_s *i2c;               /* I2C interface */
+  struct pcf8574_lcd_backpack_config_s cfg;   /* gpio configuration */
+  uint8_t bl_bit;                             /* current backlight bit */
+  sem_t sem_excl;                             /* mutex */
+};
+
+struct lcd_instream_s
+{
+  struct lib_instream_s stream;
+  FAR const char *buffer;
+  ssize_t nbytes;
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Character driver methods */
+
+static int pcf8574_lcd_open(FAR struct file *filep);
+static int pcf8574_lcd_close(FAR struct file *filep);
+static ssize_t pcf8574_lcd_read(FAR struct file *filep, FAR char *buffer,
+                                size_t buflen);
+static ssize_t pcf8574_lcd_write(FAR struct file *filep,
+                                 FAR const char *buffer, size_t buflen);
+static int pcf8574_lcd_ioctl(FAR struct file *filep, int cmd,
+                             unsigned long arg);
+#ifndef CONFIG_DISABLE_POLL
+static int pcf8574lcd_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                           bool setup);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct file_operations g_pcf8574_lcd_fops =
+{
+  pcf8574_lcd_open,            /* open */
+  pcf8574_lcd_close,           /* close */
+  pcf8574_lcd_read,            /* read */
+  pcf8574_lcd_write,           /* write */
+  0,                           /* seek */
+  pcf8574_lcd_ioctl,           /* ioctl */
+#ifndef CONFIG_DISABLE_POLL
+  pcf8574lcd_poll,             /* poll */
+#endif
+  0                            /* unlink */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pca8574_write
+ *
+ * Description:
+ *  primitive I2C write operation for the PCA8574, which is the IO expander
+ *  device used on the board.  The board essentially byte-bangs the
+ *  parallel interface in nybble mode much as one might with a conventional
+ *  GPIO based interface.  The I2C interface simply sets the state of the
+ *  8 IO lines to control the 4 data, 3 control, and one for backlight,
+ *  signals.
+ *
+ ****************************************************************************/
+
+static void pca8574_write(FAR struct pcf8574_lcd_dev_s *priv, uint8_t data)
+{
+  struct i2c_config_s config;
+  int ret;
+
+  /* Set up the I2C configuration */
+
+  config.frequency = I2C_FREQ;
+  config.address   = priv->cfg.addr;
+  config.addrlen   = 7;
+
+  /* Write the value */
+
+  ret = i2c_write(priv->i2c, &config, &data, 1);
+  if (ret < 0)
+    {
+      lcdvdbg("pca8574_write() failed: %d\n", ret);
+      return;
+    }
+
+  return;
+}
+
+/****************************************************************************
+ * Name: pca8574_read
+ *
+ * Description:
+ *  primitive I2C read operation for the PCA8574, which is the IO expander
+ *  device used on the board.  The PCF8574 is 'interesting' in that it doesn't
+ *  really have a data direction register, but instead the outputs are current-
+ *  limited when high, so by setting an IO line high, you are also making it
+ *  an input.  Consequently, before using this method, you'll need to perform a
+ *  pca8574_write() setting the bits you are interested in reading to 1's,
+ *  then call this method.
+ *
+ ****************************************************************************/
+
+static int pca8574_read(FAR struct pcf8574_lcd_dev_s *priv, uint8_t* data)
+{
+  struct i2c_config_s config;
+  int ret;
+
+  /* Set up the I2C configuration */
+
+  config.frequency = I2C_FREQ;
+  config.address   = priv->cfg.addr;
+  config.addrlen   = 7;
+
+  /* Read the value */
+
+  ret = i2c_read(priv->i2c, &config, data, 1);
+  if (ret < 0)
+    {
+      lcdvdbg("pca8574_read() failed: %d\n", ret);
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: lcd_backlight
+ *
+ * Description:
+ *  turn on, or off, the LCD backlight
+ *
+ ****************************************************************************/
+
+static void lcd_backlight(FAR struct pcf8574_lcd_dev_s *priv, bool blOn)
+{
+  uint8_t data;
+
+  data = ((blOn && priv->cfg.bl_active_high) ||
+          (!blOn && !priv->cfg.bl_active_high)) ? (1 << priv->cfg.bl) : 0;
+  pca8574_write(priv, data);
+  priv->bl_bit = data;
+}
+
+/****************************************************************************
+ * Name: rc2addr
+ *
+ * Description:
+ *  This converts a row/column pair to a screen memory address.
+ *
+ ****************************************************************************/
+
+static inline uint8_t rc2addr(FAR struct pcf8574_lcd_dev_s *priv,
+                              uint8_t row, uint8_t col)
+{
+  if (row < 2)
+    {
+      /* 1 and 2 line displays are simple; line0 @ 0x00, line1 @ 0x40 */
+
+      return row * 0x40 + col;
+    }
+  else
+    {
+      /* 4 line displays are intersting; third line really is a continuation
+       * of first line, and fourth line is a continuation of second.
+       */
+
+      return (row - 2) * 0x40 + (col - priv->cfg.cols);
+    }
+}
+
+/****************************************************************************
+ * Name: addr2rc
+ *
+ * Description:
+ *  This converts a screen memory address to a row/column pair.
+ *
+ ****************************************************************************/
+
+static inline void addr2rc(FAR struct pcf8574_lcd_dev_s *priv,
+                          uint8_t addr, uint8_t* row, uint8_t* col)
+{
+
+  *row = addr / 0x40;
+  *col = addr % 0x40;
+
+  if (*col >= priv->cfg.cols)
+    {
+      /* 4 line displays have third and fourth lines really as continuation
+       * of first and second.
+       */
+
+      *row += 2;
+      *col -= priv->cfg.cols;
+    }
+}
+
+/****************************************************************************
+ * Name: prepare_nybble
+ *
+ * Description:
+ *  This is a bit tedious, but scramble the bits of the nybble into position
+ *  as per this board's particular wiring.  Most boards are either on the
+ *  top four bits, or bottom four, so a shift would do typically in those
+ *  cases, but this gives us ultimate flexibility.
+ *
+ ****************************************************************************/
+
+uint8_t prepare_nybble(FAR struct pcf8574_lcd_dev_s *priv, uint8_t nybble)
+{
+  uint8_t lcddata = 0;
+
+  if (nybble & 0x08)
+    {
+      lcddata |= (1 << priv->cfg.d7);
+    }
+
+  if (nybble & 0x04)
+    {
+      lcddata |= (1 << priv->cfg.d6);
+    }
+
+  if (nybble & 0x02)
+    {
+      lcddata |= (1 << priv->cfg.d5);
+    }
+
+  if (nybble & 0x01)
+    {
+      lcddata |= (1 << priv->cfg.d4);
+    }
+
+  return lcddata;
+}
+
+/****************************************************************************
+ * Name: unprepare_nybble
+ *
+ * Description:
+ *  This is the opposite of prepare_nybble(), and is used to unscramble bits
+ *  when reading data from the display, as per board wiring.
+ *
+ ****************************************************************************/
+
+uint8_t unprepare_nybble(FAR struct pcf8574_lcd_dev_s *priv, uint8_t lcddata)
+{
+  uint8_t data = 0;
+
+  if (lcddata & (1 << priv->cfg.d7))
+    {
+      data |= 0x08;
+    }
+
+  if (lcddata & (1 << priv->cfg.d6))
+    {
+      data |= 0x04;
+    }
+
+  if (lcddata & (1 << priv->cfg.d5))
+    {
+      data |= 0x02;
+    }
+
+  if (lcddata & (1 << priv->cfg.d4))
+    {
+      data |= 0x01;
+    }
+
+  return data;
+}
+
+/****************************************************************************
+ * Name: latch_nybble
+ *
+ * Description:
+ *  Latch a nybble on the LCD bus.  This is done for each of two halves of a
+ *  write operation in 4-bit mode.  The 'rs' param is false for command
+ *  transfers, and true for data transfers.
+ *
+ ****************************************************************************/
+
+static void latch_nybble(FAR struct pcf8574_lcd_dev_s *priv, uint8_t nybble,
+                         bool rs)
+{
+  uint8_t lcddata;
+  uint8_t en_bit;
+  uint8_t rs_bit;
+
+  en_bit = 1 << priv->cfg.en;
+  rs_bit = rs ? (1 << priv->cfg.rs) : 0;
+
+  /* Put the nybble, preserving backlight, reset R/~W and set EN and maybe RS */
+
+  lcddata = prepare_nybble(priv, nybble) | priv->bl_bit | en_bit | rs_bit;
+  pca8574_write(priv, lcddata);
+  up_udelay(DELAY_US_NYBBLE0);  /* setup */
+
+  /* Latch on EN falling edge */
+
+  lcddata &= ~en_bit;
+  pca8574_write(priv, lcddata);
+  up_udelay(DELAY_US_NYBBLE1);  /* hold */
+}
+
+/****************************************************************************
+ * Name: load_nybble
+ *
+ * Description:
+ *  Load a nybble from the LCD bus.  This is done for each of two halves of a
+ *  read operation in 4-bit mode.  The 'rs' param is false for command
+ *  transfers (the only one is to read status and the address register), and
+ *  true for data transfers.
+ *
+ ****************************************************************************/
+
+static uint8_t load_nybble(FAR struct pcf8574_lcd_dev_s *priv, bool rs)
+{
+  uint8_t lcddata;
+  uint8_t en_bit;
+  uint8_t rs_bit;
+  uint8_t rw_bit;
+  uint8_t data;
+
+  en_bit = 1 << priv->cfg.en;
+  rs_bit = rs ? (1 << priv->cfg.rs) : 0;
+  rw_bit = 1 << priv->cfg.rw;
+
+  /* Put highs on the data lines, preserve, set R/~W and set EN and maybe RS */
+
+  lcddata = prepare_nybble(priv, 0x0f) | priv->bl_bit | en_bit | rw_bit | rs_bit;
+  pca8574_write(priv, lcddata);
+  up_udelay(DELAY_US_NYBBLE0);  /* setup */
+
+  /* Now read the data */
+
+  pca8574_read(priv, &data);
+  data = unprepare_nybble(priv, data);
+
+  /* Transaction completed on EN falling edge */
+
+  lcddata &= ~en_bit;
+  pca8574_write(priv, lcddata);
+  up_udelay(DELAY_US_NYBBLE1);  /* hold */
+
+  return data;
+}
+
+/****************************************************************************
+ * Name: lcd_putcmd
+ *
+ * Description:
+ *  Write a command to the LCD.  Most of the time this is done in nybble
+ *  mode in two phases, but in special cases (like initialization) we do not
+ *  do two phases.
+ *
+ ****************************************************************************/
+
+static void lcd_putcmd(FAR struct pcf8574_lcd_dev_s *priv, uint8_t data)
+{
+  latch_nybble(priv, data >> 4, false);
+  latch_nybble(priv, data, false);
+  up_udelay(DELAY_US_WRITE);
+}
+
+/****************************************************************************
+ * Name: lcd_putdata
+ *
+ * Description:
+ *  Write a byte to the LCD.  This is used both for screen data and for
+ *  character generator data, depending on a previous command that selected
+ *  which ever is the destination.
+ *
+ ****************************************************************************/
+
+static inline void lcd_putdata(FAR struct pcf8574_lcd_dev_s *priv,
+                               uint8_t data)
+{
+  latch_nybble(priv, data >> 4, true);
+  latch_nybble(priv, data, true);
+  up_udelay(DELAY_US_WRITE);
+}
+
+/****************************************************************************
+ * Name: lcd_getdata
+ *
+ * Description:
+ *  Read a data byte from the LCD.
+ *
+ ****************************************************************************/
+
+static inline uint8_t lcd_getdata(FAR struct pcf8574_lcd_dev_s *priv)
+{
+  uint8_t data;
+  data = (load_nybble(priv, true) << 4) | load_nybble(priv, true);
+  return data;
+}
+
+/****************************************************************************
+ * Name: lcd_getcmd
+ *
+ * Description:
+ *  Read a command byte from the LCD.  There really is only one such read:
+ *  get 'busy' status, and current address value.
+ *
+ ****************************************************************************/
+
+static inline uint8_t lcd_getcmd(FAR struct pcf8574_lcd_dev_s *priv)
+{
+  uint8_t data;
+  data = (load_nybble(priv, false) << 4) | load_nybble(priv, false);
+  return data;
+}
+
+/****************************************************************************
+ * Name: lcd_read_busy_addr
+ *
+ * Description:
+ *  Read the busy flag, and, optionally, the current value of the address
+ *  register (data or character generator dependent on a previous command).
+ *
+ ****************************************************************************/
+
+static bool lcd_read_busy_addr(FAR struct pcf8574_lcd_dev_s *priv, uint8_t* addr)
+{
+  uint8_t data = lcd_getcmd(priv);
+
+  if (NULL != addr)
+    {
+      *addr = data & 0x7f;
+    }
+
+  return (data & 0x80) ? true : false;
+}
+
+/****************************************************************************
+ * Name: lcd_init
+ *
+ * Description:
+ *  perform the initialization sequence to get the LCD into a known state.
+ *
+ ****************************************************************************/
+
+static void lcd_init(FAR struct pcf8574_lcd_dev_s *priv)
+{
+  /* Wait for more than 15 ms after Vcc for the LCD to stabilize */
+
+  usleep(20000);
+
+  /* Perform the init sequence.  This sequence of commands is constructed so
+   * that it will get the device into nybble mode irrespective of what state
+   * the device is currently in (could be 8 bit, 4 bit nyb 0, 4 bit nyb 1).
+   * By sending the 'set 8-bit mode' three times, we will definitely end up
+   * in 8 bit mode, and then we can reliably transition to 4 bit mode for
+   * the remainder of operations.
+   */
+
+  /* Send Command 0x30, set 8-bit mode, and wait > 4.1 ms*/
+
+  latch_nybble(priv, 0x30>>4, false);
+  usleep(5000);
+
+  /* Send Command 0x30, set 8-bit mode, and wait > 100 us */
+
+  latch_nybble(priv, 0x30>>4, false);
+  usleep(200);
+
+  /* Send Command 0x30, set 8-bit mode */
+
+  latch_nybble(priv, 0x30>>4, false);
+
+  /* now Function set: Set interface to be 4 bits long (only 1 cycle write for the first time). */
+
+  latch_nybble(priv, 0x20>>4, false);
+
+  /* Function set: DL=0;Interface is 4 bits, N=1 (2 Lines), F=0 (5x8 dots font) */
+  
+  lcd_putcmd(priv, 0x28);
+
+  /* Display Off: D=0 (Display off), C=0 (Cursor Off), B=0 (Blinking Off) */
+  
+  lcd_putcmd(priv, 0x08);
+
+  /* Display Clear */
+  
+  lcd_putcmd(priv, CMD_CLEAR);
+  up_udelay(DELAY_US_HOMECLEAR);  /* clear needs extra time */
+
+  /* Entry Mode Set: I/D=1 (Increment), S=0 (No shift) */
+  
+  lcd_putcmd(priv, 0x06);
+
+  /* Display On, Cursor Off */
+  
+  lcd_putcmd(priv, 0x0C);
+}
+
+/****************************************************************************
+ * Name: lcd_create_char
+ *
+ * Description:
+ *  This creates a custom character pattern.  There can be 8 5x8 patterns.
+ *  The bitmap proceeds top to bottom, msb-lsb, and is right justified (i.e.
+ *  only bits 4-0 are used).  By convention, you are meant to always leave the
+ *  last line (byte) zero so that the cursor can use this line, but this is
+ *  not strictly required.
+ *
+ * Parameters:
+ *  priv - device instance
+ *  idxchar - which character is being imaged; 0 - 7
+ *  chardata - the character image bitmap; must be 8 bytes always
+ *
+ ****************************************************************************/
+
+static void lcd_create_char(FAR struct pcf8574_lcd_dev_s *priv,
+                            uint8_t idxchar, const uint8_t *chardata)
+{
+  int nIdx;
+  uint8_t addr;
+  
+  (void)lcd_read_busy_addr(priv, &addr);
+  lcd_putcmd(priv, CMD_SET_CGADDR | (idxchar << 3));  /* set CGRAM address */
+
+  for (nIdx = 0; nIdx < 8; ++nIdx)
+    {
+      lcd_putdata(priv, chardata[nIdx]);
+    }
+
+  lcd_putcmd(priv, CMD_SET_DDADDR | addr);            /* restore DDRAM address */
+}
+
+/****************************************************************************
+ * Name: lcd_set_curpos
+ *
+ * Description:
+ *  This sets the cursor position based on row, column addressing.
+ *
+ * Parameters:
+ *  priv - device instance
+ *  row - row position
+ *  col - column position
+ *
+ ****************************************************************************/
+
+static void lcd_set_curpos(FAR struct pcf8574_lcd_dev_s *priv,
+                           uint8_t row, uint8_t col)
+{
+  uint8_t addr;
+  addr = rc2addr(priv, row, col);
+  lcd_putcmd(priv, CMD_SET_DDADDR | addr);            /* set DDRAM address */
+}
+
+/****************************************************************************
+ * Name: lcd_get_curpos
+ *
+ * Description:
+ *  This gets the cursor position based on row, column addressing.
+ *
+ * Parameters:
+ *  priv - device instance
+ *  row - row position
+ *  col - column position
+ *
+ ****************************************************************************/
+
+static void lcd_get_curpos(FAR struct pcf8574_lcd_dev_s *priv,
+                           uint8_t *row, uint8_t *col)
+{
+  uint8_t addr;
+
+  (void)lcd_read_busy_addr(priv, &addr);
+  addr2rc(priv, addr, row, col);
+}
+
+/****************************************************************************
+ * Name: lcd_scroll_up
+ *
+ * Description:
+ *  Scroll the display up, and clear the new (last) line.
+ *
+ ****************************************************************************/
+
+static void lcd_scroll_up(FAR struct pcf8574_lcd_dev_s *priv)
+{
+  uint8_t *data;
+  int nRow;
+  int nCol;
+
+  data = (uint8_t *)malloc(priv->cfg.cols);
+  if (NULL == data)
+    {
+       lcdvdbg("Failed to allocate buffer in lcd_scroll_up()\n");
+      return;
+    }
+
+  for (nRow = 1; nRow < priv->cfg.rows; ++nRow)
+    {
+      lcd_set_curpos(priv, nRow, 0);
+      for (nCol = 0; nCol < priv->cfg.cols; ++nCol)
+        {
+          data[nCol] = lcd_getdata(priv);
+        }
+
+      lcd_set_curpos(priv, nRow - 1, 0);
+      for (nCol = 0; nCol < priv->cfg.cols; ++nCol)
+        {
+          lcd_putdata(priv, data[nCol]);
+        }
+    }
+
+  lcd_set_curpos(priv, priv->cfg.rows - 1, 0);
+  for (nCol = 0; nCol < priv->cfg.cols; ++nCol)
+    {
+      lcd_putdata(priv, ' ');
+    }
+
+  lcd_set_curpos(priv, priv->cfg.rows - 1, 0);
+
+  free(data);
+  return;
+}
+
+/****************************************************************************
+ * Name: lcd_codec_action
+ *
+ * Description:
+ *  Perform an 'action' as per the Segment LCD codec.
+ *
+ * Parameters:
+ *  priv - device instance
+ *  code - SLCD code action code
+ *  count - count param for those actions that take it
+ *
+ ****************************************************************************/
+
+static void lcd_codec_action(FAR struct pcf8574_lcd_dev_s *priv,
+                             enum slcdcode_e code, uint8_t count)
+{
+  switch (code)
+    {
+      /* Erasure */
+
+      case SLCDCODE_BACKDEL:         /* Backspace (backward delete) N characters */
+        {
+          if (count <= 0) /* silly case */
+            break;
+            
+          else
+            {
+              uint8_t row;
+              uint8_t col;
+
+              lcd_get_curpos(priv, &row, &col);
+              if (count > col)    /* saturate to preceding columns available */
+                {
+                  count = col;
+                }
+
+              lcd_set_curpos(priv, row, col-count);
+            }
+
+          /* ... and conscientiously fall through to next case ... */
+        }
+
+      case SLCDCODE_FWDDEL:          /* Delete (forward delete) N characters, moving text */
+        {
+          if (count <= 0) /* silly case */
+            {
+              break;
+            }
+
+          else
+            {
+              uint8_t row;
+              uint8_t col;
+              uint8_t start;
+              uint8_t end;
+              uint8_t nIdx;
+              uint8_t data;
+
+              lcd_get_curpos(priv, &row, &col);
+              start = col + count;
+
+              if (start >= priv->cfg.cols)    /* silly case of nothing left */
+                {
+                  break;
+                }
+
+              end = start + count;
+              if (end > priv->cfg.cols)      /* saturate */
+                {
+                  end = priv->cfg.cols;
+                }
+
+              for(nIdx = col; nIdx < end; ++start, ++nIdx) /* much like memmove */
+                {
+                  lcd_set_curpos(priv, row, start);
+                  data = lcd_getdata(priv);
+                  lcd_set_curpos(priv, row, nIdx);
+                  lcd_putdata(priv, data);
+                }
+
+              for(;nIdx < priv->cfg.cols; ++nIdx) /* much like memset */
+                {
+                  lcd_putdata(priv, ' ');
+                }
+
+              lcd_set_curpos(priv, row, col);
+            }
+        }
+        break;
+
+      case SLCDCODE_ERASE:           /* Erase N characters from the cursor position */
+        if (count > 0)
+          {
+          uint8_t row;
+          uint8_t col;
+          uint8_t end;
+          uint8_t nIdx;
+
+          lcd_get_curpos(priv, &row, &col);
+          end = col + count;
+          if (end > priv->cfg.cols)
+            {
+              end = priv->cfg.cols;
+            }
+          
+          for (nIdx = col; nIdx < end; ++nIdx)
+            {
+              lcd_putdata(priv, ' ');
+            }
+          
+          lcd_set_curpos(priv, row, col);
+          }
+        break;
+
+      case SLCDCODE_CLEAR:           /* Home the cursor and erase the entire display */
+        {
+          lcd_putcmd(priv, CMD_CLEAR);
+          up_udelay(DELAY_US_HOMECLEAR);  /* clear needs extra time */
+        }
+        break;
+
+      case SLCDCODE_ERASEEOL:        /* Erase from the cursor position to the end of line */
+        {
+          uint8_t row;
+          uint8_t col;
+          uint8_t nIdx;
+
+          lcd_get_curpos(priv, &row, &col);
+          
+          for (nIdx = col; nIdx < priv->cfg.cols; ++nIdx)
+            {
+              lcd_putdata(priv, ' ');
+            }
+          
+          lcd_set_curpos(priv, row, col);
+        }
+        break;
+
+      /* Cursor movement */
+
+      case SLCDCODE_LEFT:            /* Cursor left by N characters */
+        {
+          uint8_t row;
+          uint8_t col;
+
+          lcd_get_curpos(priv, &row, &col);
+          if (count > col)
+            {
+              col = 0;
+            }
+          else
+            {
+              col -= count;
+            }
+
+          lcd_set_curpos(priv, row, col);
+        }
+        break;
+
+      case SLCDCODE_RIGHT:           /* Cursor right by N characters */
+        {
+          uint8_t row;
+          uint8_t col;
+
+          lcd_get_curpos(priv, &row, &col);
+          col += count;
+          if (col >= priv->cfg.cols)
+            {
+              col = priv->cfg.cols-1;
+            }
+
+          lcd_set_curpos(priv, row, col);
+        }
+        break;
+
+      case SLCDCODE_UP:              /* Cursor up by N lines */
+        {
+          uint8_t row;
+          uint8_t col;
+
+          lcd_get_curpos(priv, &row, &col);
+          if (count > row)
+            {
+              row = 0;
+            }
+          else
+            {
+              row -= count;
+            }
+
+          lcd_set_curpos(priv, row, col);
+        }
+        break;
+
+      case SLCDCODE_DOWN:            /* Cursor down by N lines */
+        {
+          uint8_t row;
+          uint8_t col;
+
+          lcd_get_curpos(priv, &row, &col);
+          row += count;
+          if (row >= priv->cfg.rows)
+            {
+              row = priv->cfg.rows - 1;
+            }
+
+          lcd_set_curpos(priv, row, col);
+        }
+        break;
+
+      case SLCDCODE_HOME:            /* Cursor home */
+        {
+          uint8_t row;
+          uint8_t col;
+
+          lcd_get_curpos(priv, &row, &col);
+          lcd_set_curpos(priv, row, 0);
+        }
+        break;
+        
+      case SLCDCODE_END:             /* Cursor end */
+        {
+          uint8_t row;
+          uint8_t col;
+
+          lcd_get_curpos(priv, &row, &col);
+          lcd_set_curpos(priv, row, priv->cfg.cols - 1);
+        }
+        break;
+
+      case SLCDCODE_PAGEUP:          /* Cursor up by N pages */
+      case SLCDCODE_PAGEDOWN:        /* Cursor down by N pages */
+        break;                       /* Not supportable on this SLCD */
+
+      /* Blinking */
+
+      case SLCDCODE_BLINKSTART:      /* Start blinking with current cursor position */
+        lcd_putcmd(priv, CMD_CURSOR_ON_BLINK);
+        break;
+
+      case SLCDCODE_BLINKEND:        /* End blinking after the current cursor position */
+      case SLCDCODE_BLINKOFF:        /* Turn blinking off */
+        lcd_putcmd(priv, CMD_CURSOR_OFF);
+        break;                       /* Not implemented */
+
+      /* These are actually unreportable errors */
+
+      default:
+      case SLCDCODE_NORMAL:          /* Not a special keycode */
+        break;
+    }
+}
+
+/****************************************************************************
+ * Name: lcd_getstream
+ *
+ * Description:
+ *   Get one character from the LCD codec stream.
+ *
+ ****************************************************************************/
+
+static int lcd_getstream(FAR struct lib_instream_s *instream)
+{
+  FAR struct lcd_instream_s *lcdstream = (FAR struct lcd_instream_s *)instream;
+
+  if (lcdstream->nbytes > 0)
+    {
+      lcdstream->nbytes--;
+      lcdstream->stream.nget++;
+      return (int)*lcdstream->buffer++;
+    }
+
+  return EOF;
+}
+
+/****************************************************************************
+ * Name: pcf8574_lcd_open
+ *
+ * Description:
+ *   requisite device 'open' method; we don't do anything special
+ *
+ ****************************************************************************/
+
+static int pcf8574_lcd_open(FAR struct file *filep)
+{
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pcf8574_lcd_close
+ *
+ * Description:
+ *   requisite device 'close' method; we don't do anything special
+ *
+ ****************************************************************************/
+
+static int pcf8574_lcd_close(FAR struct file *filep)
+{
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pcf8574_lcd_read
+ *
+ * Description:
+ *  This simply reads as much of the display memory as possible.  This is
+ *  probably not very interesting.
+ *
+ ****************************************************************************/
+
+static ssize_t pcf8574_lcd_read(FAR struct file *filep, FAR char *buffer,
+                                size_t buflen)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+  int nIdx;
+  uint8_t addr;
+  uint8_t row;
+  uint8_t col;
+
+  sem_wait(&priv->sem_excl);
+
+  /* Get current cursor position so we can restore it */
+
+  (void)lcd_read_busy_addr(priv, &addr);
+  addr2rc(priv, addr, &row, &col);
+
+  /* Just read the entire display into the given buffer, as much as possible */
+
+  nIdx = 0;
+  row = 0;
+  col = 0;
+
+  while (nIdx < buflen && row < priv->cfg.rows)
+  {
+    if (0 == col)
+      {
+        lcd_set_curpos(priv, row, 0);
+      }
+
+    buffer[nIdx] = lcd_getdata(priv);
+
+    ++nIdx;
+    ++col;
+    if (priv->cfg.cols == col)
+      {
+        ++row;
+        col = 0;
+      }
+  }
+
+  lcd_putcmd(priv, CMD_SET_DDADDR | addr);            /* Restore DDRAM address */
+
+  sem_post(&priv->sem_excl);
+  return nIdx;
+}
+
+/****************************************************************************
+ * Name: pcf8574_lcd_write
+ *
+ * Description:
+ *   Output a sequence of characters to the device.
+ *
+ ****************************************************************************/
+
+static ssize_t pcf8574_lcd_write(FAR struct file *filep,
+                                 FAR const char *buffer,
+                                 size_t buflen)
+{
+  FAR struct inode *inode = filep->f_inode;
+  FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+  struct lcd_instream_s instream;
+  uint8_t row;
+  uint8_t col;
+  struct slcdstate_s state;
+  enum slcdret_e result;
+  uint8_t ch;
+  uint8_t count;
+
+  sem_wait(&priv->sem_excl);
+
+  /* Initialize the stream for use with the SLCD CODEC */
+
+  instream.stream.get  = lcd_getstream;
+  instream.stream.nget = 0;
+  instream.buffer      = buffer;
+  instream.nbytes      = buflen;
+
+  /* Get the current cursor position now; we'll keep track of it as we go */
+
+  lcd_get_curpos(priv, &row, &col);
+
+  /* Now decode and process every byte in the input buffer */
+
+  memset(&state, 0, sizeof(struct slcdstate_s));
+  while ((result = slcd_decode(&instream.stream, &state, &ch, &count)) != SLCDRET_EOF)
+    {
+      if (result == SLCDRET_CHAR)          /* A normal character was returned */
+        {
+          /* Check for ASCII control characters */
+
+          if (ch == ASCII_TAB)
+            {
+              lcd_putcmd(priv, CMD_CURSOR_ON_BLINK);
+            }
+          else if (ch == ASCII_VT)
+            {
+              /* Turn the backlight on */
+
+              lcd_backlight(priv, true);
+            }
+          else if (ch == ASCII_FF)
+            {
+              /* Turn the backlight off */
+
+              lcd_backlight(priv, false);
+            }
+          else if (ch == ASCII_CR)
+            {
+              /* Perform a Home */
+
+              lcd_putcmd(priv, CMD_HOME);
+              up_udelay(DELAY_US_HOMECLEAR);  /* home needs extra time */
+              row = 0;
+              col = 0;
+            }
+          else if (ch == ASCII_SO)
+            {
+              lcd_putcmd(priv, CMD_CURSOR_OFF);
+            }
+          else if (ch == ASCII_SI)
+            {
+              /* Perform the re-initialize */
+
+              lcd_init(priv);
+              row = 0;
+              col = 0;
+            }
+          else if (ch == ASCII_LF)
+            {
+              /* unixian line term; go to start of next line */
+
+              row += 1;
+              if (row >= priv->cfg.rows)
+                {
+                  lcd_scroll_up(priv);
+                  row = priv->cfg.rows - 1;
+                }
+
+              col = 0;
+              lcd_set_curpos(priv, row, col);
+            }
+          else if (ch == ASCII_BS)
+            {
+              /* Perform the backward deletion */
+
+              lcd_codec_action(priv, SLCDCODE_BACKDEL, 1);
+
+              lcd_get_curpos(priv, &row, &col);
+            }
+          else if (ch == ASCII_DEL)
+            {
+              /* Perform the forward deletion */
+
+              lcd_codec_action(priv, SLCDCODE_FWDDEL, 1);
+
+              lcd_get_curpos(priv, &row, &col);
+            }
+          else
+            {
+              /* All others are fair game.  See if we need to wrap line. */
+
+              if (col >= priv->cfg.cols)
+              {
+                row += 1;
+                if (row >= priv->cfg.rows)
+                  {
+                    lcd_scroll_up(priv);
+                    row = priv->cfg.rows - 1;
+                  }
+
+                col = 0;
+                lcd_set_curpos(priv, row, col);
+              }
+
+              lcd_putdata(priv, ch);
+              ++col;
+            }
+        }
+      else /* (result == SLCDRET_SPEC) */  /* A special SLCD action was returned */
+        {
+          lcd_codec_action(priv, (enum slcdcode_e)ch, count);
+
+          /* we can't know what happened, so it's easier just to re-inquire
+           * as to where we are.
+           */
+
+          lcd_get_curpos(priv, &row, &col);
+        }
+    }
+
+  sem_post(&priv->sem_excl);
+  return buflen;
+}
+
+/****************************************************************************
+ * Name: pcf8574_lcd_ioctl
+ *
+ * Description:
+ *   Perform device operations that are outside the standard I/O model.
+ *
+ ****************************************************************************/
+
+static int pcf8574_lcd_ioctl(FAR struct file *filep, int cmd,
+                             unsigned long arg)
+{
+  switch (cmd)
+    {
+      case SLCDIOC_GETATTRIBUTES: /* SLCDIOC_GETATTRIBUTES:  Get the attributes of the SLCD */
+        {
+          FAR struct inode *inode = filep->f_inode;
+          FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+          FAR struct slcd_attributes_s *attr = (FAR struct slcd_attributes_s *)((uintptr_t)arg);
+
+          lcdvdbg("SLCDIOC_GETATTRIBUTES:\n");
+
+          if (!attr)
+            {
+              return -EINVAL;
+            }
+
+          attr->nrows         = priv->cfg.rows;
+          attr->ncolumns      = priv->cfg.cols;
+          attr->nbars         = 0;
+          attr->maxcontrast   = 0;
+          attr->maxbrightness = 1;  /* 'brightness' for us is the backlight */
+        }
+        break;
+
+      case SLCDIOC_CURPOS:        /* SLCDIOC_CURPOS:  Get the SLCD cursor position */
+        {
+          FAR struct inode *inode = filep->f_inode;
+          FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+          FAR struct slcd_curpos_s *attr = (FAR struct slcd_curpos_s *)((uintptr_t)arg);
+          uint8_t row;
+          uint8_t col;
+
+          sem_wait(&priv->sem_excl);
+
+          lcd_get_curpos(priv, &row, &col);
+          attr->row = row;
+          attr->column = col;
+
+          sem_post(&priv->sem_excl);
+        }
+        break;
+
+      case SLCDIOC_GETBRIGHTNESS: /* Get the current brightness setting */
+        {
+          FAR struct inode *inode = filep->f_inode;
+          FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+          bool bOn;
+
+          bOn = (priv->bl_bit && priv->cfg.bl_active_high) || (!priv->bl_bit && !priv->cfg.bl_active_high);
+          *(int*)((uintptr_t)arg) = bOn ? 1 : 0;
+        }
+        break;
+
+      case SLCDIOC_SETBRIGHTNESS: /* Set the brightness to a new value */
+        {
+          FAR struct inode *inode = filep->f_inode;
+          FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+
+          sem_wait(&priv->sem_excl);
+          lcd_backlight(priv, arg ? true : false);
+          sem_post(&priv->sem_excl);
+        }
+        break;
+
+      case SLCDIOC_CREATECHAR:    /* Create a custom character pattern */
+        {
+          FAR struct inode *inode = filep->f_inode;
+          FAR struct pcf8574_lcd_dev_s *priv = (FAR struct pcf8574_lcd_dev_s *)inode->i_private;
+          FAR struct slcd_createchar_s *attr = (FAR struct slcd_createchar_s *)((uintptr_t)arg);
+
+          sem_wait(&priv->sem_excl);
+          lcd_create_char(priv, attr->idx, attr->bmp);
+          sem_post(&priv->sem_excl);
+        }
+        break;
+
+      case SLCDIOC_SETBAR:        /* SLCDIOC_SETBAR: Set bars on a bar display */
+      case SLCDIOC_GETCONTRAST:   /* SLCDIOC_GETCONTRAST: Get the current contrast setting */
+      case SLCDIOC_SETCONTRAST:   /* SLCDIOC_SETCONTRAST: Set the contrast to a new value */
+      default:
+        return -ENOTTY;
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: pcf8574lcd_poll
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_POLL
+static int pcf8574lcd_poll(FAR struct file *filep, FAR struct pollfd *fds,
+                           bool setup)
+{
+  if (setup)
+    {
+      /* Data is always available to be read */
+
+      fds->revents |= (fds->events & (POLLIN|POLLOUT));
+      if (fds->revents != 0)
+        {
+          sem_post(fds->sem);
+        }
+    }
+
+  return OK;
+}
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pcf8574_lcd_backpack_register
+ *
+ * Description:
+ *  Register a character driver that is an I2C LCD 'backpack' for the
+ *  ever-popular HD44780 based 16x2 LCD via pcf8574 I2C IO expander.
+ *
+ ****************************************************************************/
+
+int pcf8574_lcd_backpack_register(FAR const char *devpath,
+                                  FAR struct i2c_master_s *i2c,
+                                  FAR struct pcf8574_lcd_backpack_config_s *cfg)
+{
+  FAR struct pcf8574_lcd_dev_s *priv;
+  int ret;
+  
+  /* Sanity check on geometry */
+
+  if (cfg->rows < 1 || cfg->rows > 4)
+    {
+      lcdvdbg("Display rows must be 1-4\n");
+      return -EINVAL;
+    }
+
+  if ((cfg->cols < 1 || cfg->cols > 64) || (cfg->rows == 4 && cfg->cols > 32))
+    {
+      lcdvdbg("Display cols must be 1-64, and may not be part of a 4x40 configuration\n");
+      return -EINVAL;
+    }
+
+  /* Initialize the device structure */
+
+  priv = (FAR struct pcf8574_lcd_dev_s *)kmm_malloc(sizeof(struct pcf8574_lcd_dev_s));
+  if (!priv)
+    {
+      lcdvdbg("Failed to allocate instance\n");
+      return -ENOMEM;
+    }
+
+  priv->i2c = i2c;
+  priv->cfg = *cfg;
+  priv->bl_bit = priv->cfg.bl_active_high ? 0 : (1 << priv->cfg.bl);
+  sem_init(&priv->sem_excl, 0, 1);
+
+  /* Initialize */
+
+  lcd_init(priv);
+
+  /* Register the character driver */
+
+  ret = register_driver(devpath, &g_pcf8574_lcd_fops, 0666, priv);
+  if (ret < 0)
+    {
+      lcdvdbg("Failed to register driver: %d\n", ret);
+      kmm_free(priv);
+    }
+
+  lcdvdbg("pcf8574_lcd_backpack driver loaded successfully!\n");
+  return ret;
+}
diff --git a/drivers/lcd/pcf8574_lcd_backpack_readme.txt b/drivers/lcd/pcf8574_lcd_backpack_readme.txt
new file mode 100644
index 0000000000..c7d2775d11
--- /dev/null
+++ b/drivers/lcd/pcf8574_lcd_backpack_readme.txt
@@ -0,0 +1,245 @@
+pcf8574 lcd backpack - readme.txt
+20160524a, ziggurat29
+
+Abstract
+========
+
+This describes the use of the pcf8574_lcd_backpack.h, .c driver module for NuttX.
+
+Contents
+========
+
+  o Summary for Those Who Don't Like to Read
+  o Introduction
+  o Usage
+    - Specifying the I2C Address
+    - Specifying the LCD Display Format
+    - Specifying Unknown/New Backpacks
+  o Special Features
+    - Codec
+    - Ioctl
+  o Troubleshooting
+
+Summary for Those Who Don't Like to Read
+========================================
+
+To use, in your board_app_initialize(),
+
+1)  instantiate an I2C bus:
+
+    FAR struct i2c_master_s* i2c = stm32l4_i2cbus_initialize(1);
+
+2)  set the configuration for the particular make of board, and LCD format:
+
+    struct pcf8574_lcd_backpack_config_s cfg = LCD_I2C_BACKPACK_CFG_MJKDZ;
+    cfg.rows = 2;
+    cfg.cols = 16;
+
+3)  instantiate the device on the I2C bus previously created:
+
+    ret = pcf8574_lcd_backpack_register("/dev/slcd0", i2c, &cfg);
+
+Introduction
+============
+
+The character LCD modules based on the HD44780 (and compatible ST7706U, KS0066U,
+SED1278, etc.) drivers have been around for many decades and are quite popular.
+One challenge is that they require a large number of GPIO (11 in 8-bit mode, 7
+in 4-bit mode, and an additional line if you control the backlight).
+
+To address this, several folks have created daughter boards for the LCD module
+which present a two-wire I2C interface.  Generally, folks call these interface
+boards an 'lcd backpack'.  A large class of them (and in particular, the very
+inexpensive ones found on ebay, q.v. google "ebay i2c lcd backpack"; they're
+usually about $USD 1), use the same design:  a PCF8574 I2C IO expander.
+Variations occur in mapping GPIO line to LCD pins, but otherwise the
+expectation is that you control the LCD at a low-level tweaking the lines
+("byte-banging"?)
+
+My original motivation for producing this was to simply serve as a test device
+for some I2C driver work I was doing, but it occurred to me that it may be
+useful to others, given the popularity of the 'lcd backpack', so I cleaned up
+the code and made it general to support all the variations on the market, and
+also to adopt the NuttX notion of a 'segment lcd codec', which is used to
+transport escape sequences (for doing things like clearing the display, turning
+on/off the cursor, etc), and also the standard ioctls.
+
+I believe it should support all "lcd backpack"s on the market (because you can
+specify the particular wiring), and all HD44780-based LCD modules in 1-line,
+2-line, and 4-line configurations (except 4x40 -- this is not supported by
+the hardware).
+
+This module should be cpu-architecture-neutral, and work with any standard I2C
+bus object.  At the time of this writing it has been tested only with the
+STM32L4 chip and with the 'MJKDZ' backpack board with a 16x2 lcd module.
+
+Usage
+=====
+
+The driver is contained in the files pcf8574_lcd_backpack.h and
+pcf8574_lcd_backpack.c; you can include these in your build in whatever manner
+you choose (e.g. copy them into your board's src directory, and reference them
+in the Makefile).
+
+As with other I2C devices, you first instantiate the I2C bus, and then
+instantiate the driver on that bus.  When instantiating the driver, you also
+provide a configuration 'descriptor' that specified board wiring and LCD
+format parameters.  You can explicitly specify any wiring configuration, and
+some known popular boards are already #defined for your convenience.
+
+E.g.:
+
+    #include <nuttx/i2c/i2c_master.h>
+    #include "pcf8574_lcd_backpack.h"
+    
+    #define MJKDZ_I2C_PORTNO 1
+    #define MJKDZ_DEVICE_NAME "/dev/lcd0"
+
+    FAR struct i2c_master_s* g_i2cMJKDZ = NULL;
+
+    ....
+
+    g_i2cMJKDZ = stm32l4_i2cbus_initialize(MJKDZ_I2C_PORTNO);
+
+    ....
+
+    struct pcf8574_lcd_backpack_config_s cfg = LCD_I2C_BACKPACK_CFG_MJKDZ;
+    cfg.rows = 2;
+    cfg.cols = 16;
+
+    ret = pcf8574_lcd_backpack_register(MJKDZ_DEVICE_NAME, g_i2cMJKDZ, &cfg);
+
+If all the above executes successfully, you should wind up with a character
+device node "/dev/lcd0".  Applications can open that node and write() to it,
+and the shell can emit data to it (e.g. 'echo Hi, there! > /dev/lcd0').
+
+That is the basic configuration.  Some additional configuration points are
+worth noting.
+
+Specifying the I2C Address
+--------------------------
+
+The 'struct pcf8574_lcd_backpack_config_s' shown above is initialized using
+the convenience macro LCD_I2C_BACKPACK_CFG_MJKDZ.  Those convenience macros
+use the default I2C address for the board, however many of the boards allow
+altering the address (by jumpers, or removing pullups).  You need to specify
+the correct address for your board's physical configuration.  You can do that
+via
+
+  cfg.addr = 0x23;
+
+Specifying the LCD Display Format
+---------------------------------
+
+The LCD modules cannot 'self-describe' their physical format, so it must be
+explicitly provided to the driver.  The correct format is important for
+computing screen coordinate addresses and for scrolling and line wrap.
+
+In the example above, the screen format is specifying by setting the
+fields in the configuration descriptor:
+
+  cfg.rows = 2;
+  cfg.cols = 16;
+
+The lcd backpack can accomodate all known 1-line and 2-line displays, and
+4-line displays up to 4 x 32.  Explicitly, the 4 x 40 /cannot/ be supported
+because it has an important hardware difference (it is actually two 4x20
+controllers, and the LCD backpack does not have the wiring for the
+second controller's 'E' line).  This is a hardware limitation of the
+lcd backpack, rather than the driver.
+
+Specifying Unknown/New Backpacks
+--------------------------------
+
+The descriptor initializer macros in the form  LCD_I2C_BACKPACK_CFG_xxx
+located near the top of pcf8574_lcd_backpack.h are provided for convenience.
+However, their use is not required, and it can be useful to initialize the
+descriptor with explicit values, say, for custom or unknown boards.
+
+The format of this descriptor is conscientiously chosen to be semantically
+similar to an equivalent initialization mechanism popular in the Arduino
+community used in their LCD support libraries.  It specifies:
+
+  * I2C address
+  * pin mapping for data lines
+  * pin mapping for control lines
+  * pin mapping for backlight control line
+  * polarity sense of backlight control line
+
+and we add to that
+
+  * (row, column) size of display
+
+(the Arduino libraries specify display size at a different point in code)
+You should be able to readily port a functional Arduino project by cutting-
+and-pasting the sequence of numbers that are the pin defs for the lcd
+backpack you are using.
+
+Special Features
+================
+
+Codec
+-----
+
+The driver supports the NuttX 'segment lcd codec', which facilitates the
+encoding of control functions into the write() stream.  These can be used
+to clear the display, move the cursor, etc.  For details, q.v.
+
+    nuttx/lcd/slcd_codec.h
+
+Ioctl
+-----
+
+The driver supports the NuttX ioctl definitions for segment lcd.  Q.v.
+
+    nuttx/lcd/slcd_ioctl.h
+
+Additionally, the ioctl SLCDIOC_CREATECHAR is provided to allow the
+creation of custom characters.
+
+The HD44780 devices generally support the creation of 8 custom
+characters, which map to code points 0-7.  The characters are 5x8
+pixels (with the expectation that the last row is left blank, to
+accommodate the underscore cursor, though this is not strictly a
+requirement).
+
+The SLCDIOC_CREATECHAR ioctl takes a parameter, which is a struct
+consisting of the character index being programmed (0-7) and the
+8-byte bitmap of the character image.  The bitmap is constructed
+with each byte representing a row, from top row to bottom row.
+Each row is imaged left to right, MSB to LSB, right-justified (i.e.,
+bit 4 is leftmost, bit 0 is rightmost, and bits 7-5 are unused).
+
+You may reference these characters simply by including them in
+the data you write() to the device, e.g.
+
+    write(fd, "\x01,\x02Hi, there!\n", 13);
+
+Example of programming a character image:
+
+  static const struct slcd_createchar_s custom_char =
+      { 4, { 0x04, 0x0e, 0x15, 0x04, 0x04, 0x04, 0x04, 0x00 } };  /* up arrow */
+
+  ret = ioctl(fd, SLCDIOC_CREATECHAR, (unsigned long)custom_char);
+
+Now character '\x04' will display as an 'up arrow'.
+
+Note, you might consider avoiding the use of code point 0x00 unless
+absolutely needed, because the embedded nul character can cause
+problems.  The driver, and write() apis are binary, and unaffected,
+but things like printf() and puts() assume C-style strings, and are
+affected.
+
+Troubleshooting
+===============
+
+* Check your I2C address.  turn on debugging output so you can see
+  bus timeouts that suggest a non-responsive slave.
+* Check your board wiring and configuration specification.  Buzz
+  out the lines if you have to.
+* Con't forget to check the 'contrast' potentiometer.  The voltage
+  at the central wiper should be approximately 0.29 V.  The useful
+  range of voltages at this pin is very narrow, and outside that
+  range there will be nothing visible on the display, so most of the
+  turn range of the pot is non-useful.  Much of human life has been
+  wasted in the rediscovery of this farcically idiotic 
diff --git a/include/nuttx/lcd/pcf8574_lcd_backpack.h b/include/nuttx/lcd/pcf8574_lcd_backpack.h
new file mode 100644
index 0000000000..437d71d64f
--- /dev/null
+++ b/include/nuttx/lcd/pcf8574_lcd_backpack.h
@@ -0,0 +1,176 @@
+/****************************************************************************
+ * include/nuttx/lcd/pcf8574_lcd_backpack.h
+ *
+ *   Copyright (C) 2016 Gregory Nutt. All rights reserved.
+ *   Author: dev@ziggurat29.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_LCD_PCF8574_LCD_BACKPACK_H
+#define __INCLUDE_NUTTX_LCD_PCF8574_LCD_BACKPACK_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <sys/ioctl.h>
+#include <nuttx/lcd/slcd_ioctl.h>
+#include <nuttx/lcd/slcd_codec.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configurations of some well-known boards.  You may still have to modify the
+ * address if was changed from the default.  You will also need to specify the
+ * geometry of your attached LCD display.  You can support:
+ * 1x8, 1x12, 1x16, 2x8, 2x12, 2x16, 2x20, 2x24, 2x40, 4x16, 4x20
+ * Pretty much anything on the market except 4x40, which really consists of two
+ * separate 2x40 controllers, and the I2C backpack doesn't support those due
+ * to the second 'E' line being needed.
+ * Consider these 'informative'.
+ * XXX Note, actual testing has been done on LCD_I2C_BACKPACK_CFG_MJKDZ only,
+ * the others come from online research.
+ */
+
+/* board marked 'mjkdz' */
+
+#define LCD_I2C_BACKPACK_CFG_MJKDZ {0x20,4,5,6,0,1,2,3,7,false,0,0}
+
+/* YwRobot/DFRobot/SainSmart */
+
+#define LCD_I2C_BACKPACK_CFG_SAINSMART {0x27,2,1,0,4,5,6,7,3,true,0,0}
+
+/* Robot Arduino LCM1602/2004 */
+
+#define LCD_I2C_BACKPACK_CFG_ROBOT {0x27,2,1,0,4,5,6,7,3,false,0,0}
+
+/* I2CLCDextraIO board modded for backlight (pnp transistor) */
+
+#define LCD_I2C_BACKPACK_CFG_I2CIO_PNP {0x38,6,5,4,0,1,2,3,7,false,0,0}
+
+/* I2CLCDextraIO board modded for backlight (npn transistor) */
+
+#define LCD_I2C_BACKPACK_CFG_I2CIO_NPN {0x38,6,5,4,0,1,2,3,7,true,0,0}
+
+/* SLCDIOC_CREATECHAR: Create a custom character pattern
+ *
+ * argument:  pointer to slcd_createchar_s structure (defined below)
+ */
+
+#define SLCDIOC_CREATECHAR _SLCDIOC(0x80)
+
+ /****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* Used to specify the pin wiring for this particular module */
+
+struct pcf8574_lcd_backpack_config_s
+{
+  uint8_t   addr; /* I2C address; 'unshifted' (i.e. disregarding the LSB R/W bit)
+                   * these can vary widely depending on board pullups, whether it
+                   * uses a PCF8574-T or -AT, etc.  Many default to either 0x20
+                   * or 0x27, and some default to 0x38 or 0x3f.  Check with seller.
+                   */
+  uint8_t   en;   /* gpio bit for LCD EN */
+  uint8_t   rw;   /* gpio bit for LCD RW */
+  uint8_t   rs;   /* gpio bit for LCD RS */
+  uint8_t   d4;   /* gpio bit for LCD D4 */
+  uint8_t   d5;   /* gpio bit for LCD D5 */
+  uint8_t   d6;   /* gpio bit for LCD D6 */
+  uint8_t   d7;   /* gpio bit for LCD D7 */
+  uint8_t   bl;   /* gpio bit for backlight control */
+  bool      bl_active_high;  /* is the backlight control active high? */
+  uint8_t   rows; /* screen geometry, rows, 1, 2 or 4 */
+  uint8_t   cols; /* screen geometry, cols, 8, 12, 16, 20, 24, 40 */
+};
+
+/* Used with the SLCDIOC_CREATECHAR ioctl call */
+
+struct slcd_createchar_s
+{
+  uint8_t idx;          /* Custom character index; 0-7.  Note; you'll probably
+                         * want to avoid code point 0 unless you really need it,
+                         * because embedded nul in a C string can cause surprises.
+                         */
+  uint8_t bmp[8];       /* Custom character bitmap.  The bitmap is structured as
+                         * a 5x8 bitmap.  '1' = pixel on, msb-lsb left-to-right,
+                         * and right-justified (i.e. only bits 4-0 are used).
+                         * Each byte represents 1 row.  By convention, you are
+                         * expected to leave the last row all 0's, because it is
+                         * used by the cursor, but this is not strictly required.
+                         */
+};
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pcf8574_lcd_backpack_register
+ *
+ * Description:
+ *  Register a character driver that is a I2C LCD 'backpack' based on the
+ *  PCF8574 I2C IO expander.  It allows operation of the ever-popular HD44780
+ *  based LCDs via I2C instead of parallel (saving a bunch of gpio lines).
+ *
+ *  There are a multitude of these available from various sources (e.g. ebay).
+ *  They typically vary by gpio-to-lcd pin mapping, and I2C addresss, but
+ *  otherwise are functionally identical.
+ *
+ *  The characters presented for codes depend on the masked rom of the
+ *  particular LCD device attached, but the most common are:
+ *    'A00', which present 0x20-0x7f, and 0xa0-0xff', similar to JIS X 0201
+ *    'A02', which present 0x10-0xff, and include various european symbols
+ *  In both cases, codes 0x00-0x07 map to the CGRAM characters, which can be
+ *  loaded via ioctl SLCDIOC_CREATECHAR (q.v.).
+ *
+ *  This driver supports the SLCD codec for various escape sequences; q.v.
+ *  nuttx/lcd/slcd_codec.h for details.  This driver supports the SLCD ioctl
+ *  interface for various extended commands; q.v. nuttx/lcd/slcd_ioctl.h for
+ *  details.  This driver supports an additional ioctl for defining custom
+ *  characters; see above for details.
+ *
+ * Parameters:
+ *  devpath - path to device node; arbitrary, but typically '/dev/lcd0' or such
+ *  i2c - the low-level i2c bus onto which to bind
+ *  cfg - the board-specific configuration
+ *
+ ****************************************************************************/
+
+int pcf8574_lcd_backpack_register(FAR const char *devpath,
+                                  FAR struct i2c_master_s *i2c,
+                                  FAR struct pcf8574_lcd_backpack_config_s *cfg);
+
+#endif /* __INCLUDE_NUTTX_LCD_PCF8574_LCD_BACKPACK_H */