You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2022/06/10 12:05:21 UTC

[GitHub] [incubator-nuttx] adamkaliszan opened a new pull request, #6408: e-ink ssd1680 driver

adamkaliszan opened a new pull request, #6408:
URL: https://github.com/apache/incubator-nuttx/pull/6408

   ## Summary
   E-ink driver for chipsed sssd 1680
   No BSP files
   
   ## Impact
   None
   ## Testing
   On ESP32 lilygo tty t5v2.2
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] xiaoxiang781216 merged pull request #6408: e-ink ssd1680 driver

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged PR #6408:
URL: https://github.com/apache/incubator-nuttx/pull/6408


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [incubator-nuttx] pkarashchenko commented on a diff in pull request #6408: e-ink ssd1680 driver

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #6408:
URL: https://github.com/apache/incubator-nuttx/pull/6408#discussion_r894816683


##########
include/nuttx/lcd/ssd1680.h:
##########
@@ -0,0 +1,193 @@
+/****************************************************************************
+ * include/nuttx/lcd/ssd1680.h
+ *
+ * Driver for Solomon Systech SSD1680 e-paper controller
+ *
+ * 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_LCD_SSD1680_H
+#define __INCLUDE_NUTTX_LCD_SSD1680_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdbool.h>
+
+#include <nuttx/arch.h>
+
+#ifdef CONFIG_LCD_SSD1680
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Configuration ************************************************************/
+
+/* SSD1680 configuration settings:
+ *
+ * CONFIG_SSD1680_SPIMODE - Controls the SPI mode
+ * CONFIG_SSD1680_FREQUENCY - Define to use a different bus frequency
+ *
+ * Required LCD driver settings:
+ *
+ * CONFIG_LCD_MAXCONTRAST should be 255, but any value >0 and <=255
+ *   will be accepted.
+ * CONFIG_LCD_MAXPOWER must be 1
+ *
+ * Optional LCD driver settings:
+ * CONFIG_LCD_LANDSCAPE, CONFIG_LCD_PORTRAIT, CONFIG_LCD_RLANDSCAPE, and
+ *   CONFIG_LCD_RPORTRAIT - Display orientation.
+ *
+ * Required SPI driver settings:
+ * CONFIG_SPI_CMDDATA - Include support for cmd/data selection.
+ */
+
+/* SPI Interface
+ *
+ * "The serial interface consists of serial clock SCL, serial data SI, CS and
+ *  CMD/!DTA. SI is shifted into an 8-bit shift register on every rising edge
+ *  of SCL in the order of D7, D6, ... and D0. CMD/!DTA is sampled on every
+ *  eighth clock and the data byte in the shift register is written to the
+ *  display data RAM or command register in the same clock."
+ *
+ * "This module determines whether the input data is interpreted as data or
+ * command. When CMD/!DTA = "H," the inputs at D7 - D0 are interpreted as
+ * data and be written to display RAM. When CMD/!DTA = "L", the inputs at
+ * D7 - D0 are interpreted as command, they will be decoded and be written
+ * to the corresponding command registers."
+ */
+
+#ifndef CONFIG_SPI_CMDDATA
+#  error "CONFIG_SPI_CMDDATA must be defined in your NuttX configuration"
+#endif
+
+/* Check contrast selection */
+
+#if !defined(CONFIG_LCD_MAXCONTRAST)
+#  define CONFIG_LCD_MAXCONTRAST 255
+#endif
+
+#if CONFIG_LCD_MAXCONTRAST <= 0 || CONFIG_LCD_MAXCONTRAST > 255
+#  error "CONFIG_LCD_MAXCONTRAST exceeds supported maximum"
+#endif
+
+/* Check power setting */
+
+#if !defined(CONFIG_LCD_MAXPOWER)
+#  define CONFIG_LCD_MAXPOWER 1
+#endif
+
+#if CONFIG_LCD_MAXPOWER != 1
+#  warning "CONFIG_LCD_MAXPOWER exceeds supported maximum"
+#  undef CONFIG_LCD_MAXPOWER
+#  define CONFIG_LCD_MAXPOWER 1
+#endif
+
+/* Color is 1bpp monochrome with leftmost column contained in bits 0  */
+
+#ifdef CONFIG_NX_DISABLE_1BPP
+#  warning "1 bit-per-pixel support needed"
+#endif
+
+/* Orientation */
+
+#if defined(CONFIG_LCD_LANDSCAPE)
+#  undef CONFIG_LCD_PORTRAIT
+#  undef CONFIG_LCD_RLANDSCAPE
+#  undef CONFIG_LCD_RPORTRAIT
+#elif defined(CONFIG_LCD_PORTRAIT)
+#  undef CONFIG_LCD_LANDSCAPE
+#  undef CONFIG_LCD_RLANDSCAPE
+#  undef CONFIG_LCD_RPORTRAIT
+#elif defined(CONFIG_LCD_RLANDSCAPE)
+#  undef CONFIG_LCD_LANDSCAPE
+#  undef CONFIG_LCD_PORTRAIT
+#  undef CONFIG_LCD_RPORTRAIT
+#elif defined(CONFIG_LCD_RPORTRAIT)
+#  undef CONFIG_LCD_LANDSCAPE
+#  undef CONFIG_LCD_PORTRAIT
+#  undef CONFIG_LCD_RLANDSCAPE
+#else
+#  define CONFIG_LCD_LANDSCAPE 1
+#  warning "Assuming landscape orientation"
+#endif
+
+/* Some important "colors" */
+
+#define SSD1680_Y1_BLACK  0
+#define SSD1680_Y1_WHITE  1
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+struct ssd1680_priv_s
+{
+  bool (*set_vcc) (bool on);    /* Allow board to control display power. Return
+                                 * true if request state set successfully. */
+  bool (*set_rst) (bool on);    /* Hardware reset support */
+  bool (*check_busy) (void);    /* Checks the state of busy pin */

Review Comment:
   Need to add `CODE`



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);

Review Comment:
   Need to add `FAR`



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);

Review Comment:
   Need to add `FAR`



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;

Review Comment:
   need to add `FAR`



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;

Review Comment:
   ditto



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};

Review Comment:
   C89 incompatible



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("contrast: %d\n", priv->contrast);
+  return priv->contrast;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setcontrast
+ *
+ * Description:
+ *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+
+  lcdinfo("ignoring set constrast(%d)\n", contrast);
+  DEBUGASSERT(priv);
+
+  /* Verify the contrast value */
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (contrast > CONFIG_LCD_MAXCONTRAST)
+    {
+      return -EINVAL;
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_configuredisplay
+ *
+ * Description:
+ *   Setup LCD display.
+ *
+ ****************************************************************************/
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv)
+{
+  lcdinfo("Start\n");
+
+  /* Software Reset */
+
+  lcdinfo("Software reset: (0x%02x)\n", SSD1680_SW_RESET);
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_SW_RESET);
+
+  /* Busy wait */
+
+  ssd1680_busy_wait(priv);
+  lcdinfo("SSD1680 is ready\n");
+
+  /* Step 1: Driver Output Control 3 bytes of data:
+   * - A[8:0] MUX Gate lines
+   * - B[2:0] sequence
+   * last data byte depends on connection between display and controller
+   */
+
+  lcdinfo("Set the driver output controll (0x%02x): %d 0x%02x\n",
+      SSD1680_DRIVER_CONTROL, SSD1680_DEV_NATIVE_YRES - 1,
+      SSD1680_DEV_GATE_LAYOUT);
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_DRIVER_CONTROL,
+     (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+     (SSD1680_DEV_NATIVE_YRES - 1) >> 8,
+     SSD1680_DEV_GATE_LAYOUT);
+
+  /* Step 2: SSD1680_BOOST_SOFTSTART 0x0C D7 D6 9D */
+
+  lcdinfo("Set boost soft start\n");
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_BOOST_SOFTSTART,
+      0xd7, 0xd6, 0x9d);
+
+  /* Step 3: Vcom Voltage SSD1680_WRITE_VCOM, 0x36 */
+
+  lcdinfo("Set Vcom voltage (0x%02x): 0x%2x\n", SSD1680_WRITE_VCOM, 0xa8);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_WRITE_VCOM, 0xa8);
+
+  /* Step 4: Sending undocumented command: 0x3a with data 1A */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3a, 0x1a);
+  ssd1680_snd_cmd_with_data1(priv, 0x3a, 0x1a);
+
+  /* Step 5: Sending undocumented command: 0x3b with data 08 */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3b, 0x08);
+  ssd1680_snd_cmd_with_data1(priv, 0x3b, 0x08);
+
+  /* Step 6: Data entry mode SSD1680_DATA_MODE, 0x03
+   * TODO w arduino była wartość 0x01
+   */
+
+  lcdinfo("Set data entry mode (0x%02x): 0x%2x\n",
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+  ssd1680_snd_cmd_with_data1(priv,
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+
+  /* Step 7: Set ram X start/end postion 00 FF */
+
+  lcdinfo("Set ram X start/end position (0x%02x): 0, %d\n",
+      SSD1680_SET_RAMXPOS, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMXPOS,
+      0x00, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+
+  /* Step 8: Set ram Y start/end postion
+   * TODO w adruino zamieniona start ze stopem
+   */
+
+  lcdinfo("Set ram Y start/end position (%x): 0, %d\n",
+      SSD1680_SET_RAMYPOS, SSD1680_DEV_NATIVE_YRES - 1);
+  ssd1680_snd_cmd_with_data4(priv, SSD1680_SET_RAMYPOS,
+      0x00, 0x00,
+      (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+      (SSD1680_DEV_NATIVE_YRES - 1) >> 8);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 11: Lookup table */
+
+  lcdinfo("Write lookup table (%d bytes)\n", sizeof (ssd1680_lut));
+  ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_LUT, ssd1680_lut,
+      sizeof (ssd1680_lut));
+
+  /* Step 12: Write sequence */
+
+  lcdinfo("Write controll sequence 0x%02x\n", 0xc0);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc0);
+
+  /* Step 13: Master Activate and busy wait */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  lcdinfo("Configuration ready\n");
+  priv->is_conf = true;
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_redrawfb
+ *
+ * Description:
+ *   Redraw full framebuffer to display
+ *
+ * Input Parameters:
+ *   priv   - Reference to private driver structure
+ *
+ * Assumptions:
+ *   Caller has selected the OLED section.
+ *
+ ****************************************************************************/
+
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv)
+{
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+#if defined(CONFIG_LCD_PORTRAIT) | defined(CONFIG_LCD_RLANDSCAPE)
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+#elif defined (CONFIG_LCD_LANDSCAPE)
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+#elif defined (CONFIG_LCD_RORTRAIT)
+#error "Not implemented LSC orientation"
+#endif
+
+  /* Step 14: */
+
+#if SSD1680_DEV_BPP == 1
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+  ssd1680_snd_cmd_with_data(priv,
+    SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+#else
+  int line;
+#  if SSD1680_DEV_BPP == 1
+  for (line = 0; line < SSD1680_DEV_FB_YRES; line += 8)
+    {
+      ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1,
+          priv->shadow_fb + line * SSD1680_DEV_NATIVE_YRES,
+          SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE);
+    }
+#  else
+#    error "3 color mode not implemented yet"
+#  endif
+#endif
+
+#else
+  ssd1680_snd_cmd_with_data_even_bits(priv,
+    SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 14b: */
+
+  ssd1680_snd_cmd_with_data_odd_bits(priv,
+    SSD1680_WRITE_RAM2, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+#endif
+  ssd1680_redraw_display(priv);
+  return OK;
+}
+
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv)
+{
+  /* Step 15:
+   * Set control register 2.
+   * 1 byte of data with following bits:
+   *   0x80 - enable clock signal
+   *   0x40 - enable analog
+   *   0x20 - load temperature value
+   *   0x10 - Load LUT
+   *   0x08 - Display Mode 2
+   *   0x04 - disable OSC
+   *   0x02 - disable analog
+   *   0x01 - disable clock signal
+   */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc4);
+
+  /* Step 16: */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  /* Step 18: */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc3);
+
+  /* Step 19: */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  return OK;
+}
+
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row)
+{
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+  uint8_t *src = priv->shadow_fb + row * SSD1680_DEV_ROWSIZE;

Review Comment:
   ditto



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("contrast: %d\n", priv->contrast);
+  return priv->contrast;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setcontrast
+ *
+ * Description:
+ *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+
+  lcdinfo("ignoring set constrast(%d)\n", contrast);
+  DEBUGASSERT(priv);
+
+  /* Verify the contrast value */
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (contrast > CONFIG_LCD_MAXCONTRAST)
+    {
+      return -EINVAL;
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_configuredisplay
+ *
+ * Description:
+ *   Setup LCD display.
+ *
+ ****************************************************************************/
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv)
+{
+  lcdinfo("Start\n");
+
+  /* Software Reset */
+
+  lcdinfo("Software reset: (0x%02x)\n", SSD1680_SW_RESET);
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_SW_RESET);
+
+  /* Busy wait */
+
+  ssd1680_busy_wait(priv);
+  lcdinfo("SSD1680 is ready\n");
+
+  /* Step 1: Driver Output Control 3 bytes of data:
+   * - A[8:0] MUX Gate lines
+   * - B[2:0] sequence
+   * last data byte depends on connection between display and controller
+   */
+
+  lcdinfo("Set the driver output controll (0x%02x): %d 0x%02x\n",
+      SSD1680_DRIVER_CONTROL, SSD1680_DEV_NATIVE_YRES - 1,
+      SSD1680_DEV_GATE_LAYOUT);
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_DRIVER_CONTROL,
+     (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+     (SSD1680_DEV_NATIVE_YRES - 1) >> 8,
+     SSD1680_DEV_GATE_LAYOUT);
+
+  /* Step 2: SSD1680_BOOST_SOFTSTART 0x0C D7 D6 9D */
+
+  lcdinfo("Set boost soft start\n");
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_BOOST_SOFTSTART,
+      0xd7, 0xd6, 0x9d);
+
+  /* Step 3: Vcom Voltage SSD1680_WRITE_VCOM, 0x36 */
+
+  lcdinfo("Set Vcom voltage (0x%02x): 0x%2x\n", SSD1680_WRITE_VCOM, 0xa8);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_WRITE_VCOM, 0xa8);
+
+  /* Step 4: Sending undocumented command: 0x3a with data 1A */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3a, 0x1a);
+  ssd1680_snd_cmd_with_data1(priv, 0x3a, 0x1a);
+
+  /* Step 5: Sending undocumented command: 0x3b with data 08 */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3b, 0x08);
+  ssd1680_snd_cmd_with_data1(priv, 0x3b, 0x08);
+
+  /* Step 6: Data entry mode SSD1680_DATA_MODE, 0x03
+   * TODO w arduino była wartość 0x01
+   */
+
+  lcdinfo("Set data entry mode (0x%02x): 0x%2x\n",
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+  ssd1680_snd_cmd_with_data1(priv,
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+
+  /* Step 7: Set ram X start/end postion 00 FF */
+
+  lcdinfo("Set ram X start/end position (0x%02x): 0, %d\n",
+      SSD1680_SET_RAMXPOS, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMXPOS,
+      0x00, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+
+  /* Step 8: Set ram Y start/end postion
+   * TODO w adruino zamieniona start ze stopem
+   */
+
+  lcdinfo("Set ram Y start/end position (%x): 0, %d\n",
+      SSD1680_SET_RAMYPOS, SSD1680_DEV_NATIVE_YRES - 1);
+  ssd1680_snd_cmd_with_data4(priv, SSD1680_SET_RAMYPOS,
+      0x00, 0x00,
+      (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+      (SSD1680_DEV_NATIVE_YRES - 1) >> 8);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 11: Lookup table */
+
+  lcdinfo("Write lookup table (%d bytes)\n", sizeof (ssd1680_lut));
+  ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_LUT, ssd1680_lut,
+      sizeof (ssd1680_lut));
+
+  /* Step 12: Write sequence */
+
+  lcdinfo("Write controll sequence 0x%02x\n", 0xc0);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc0);
+
+  /* Step 13: Master Activate and busy wait */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  lcdinfo("Configuration ready\n");
+  priv->is_conf = true;
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_redrawfb
+ *
+ * Description:
+ *   Redraw full framebuffer to display
+ *
+ * Input Parameters:
+ *   priv   - Reference to private driver structure
+ *
+ * Assumptions:
+ *   Caller has selected the OLED section.
+ *
+ ****************************************************************************/
+
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv)
+{
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+#if defined(CONFIG_LCD_PORTRAIT) | defined(CONFIG_LCD_RLANDSCAPE)
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+#elif defined (CONFIG_LCD_LANDSCAPE)
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+#elif defined (CONFIG_LCD_RORTRAIT)
+#error "Not implemented LSC orientation"
+#endif
+
+  /* Step 14: */
+
+#if SSD1680_DEV_BPP == 1
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+  ssd1680_snd_cmd_with_data(priv,
+    SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+#else
+  int line;
+#  if SSD1680_DEV_BPP == 1
+  for (line = 0; line < SSD1680_DEV_FB_YRES; line += 8)
+    {
+      ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1,
+          priv->shadow_fb + line * SSD1680_DEV_NATIVE_YRES,
+          SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE);
+    }
+#  else
+#    error "3 color mode not implemented yet"
+#  endif
+#endif
+
+#else
+  ssd1680_snd_cmd_with_data_even_bits(priv,
+    SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 14b: */
+
+  ssd1680_snd_cmd_with_data_odd_bits(priv,
+    SSD1680_WRITE_RAM2, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+#endif
+  ssd1680_redraw_display(priv);
+  return OK;
+}
+
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv)
+{
+  /* Step 15:
+   * Set control register 2.
+   * 1 byte of data with following bits:
+   *   0x80 - enable clock signal
+   *   0x40 - enable analog
+   *   0x20 - load temperature value
+   *   0x10 - Load LUT
+   *   0x08 - Display Mode 2
+   *   0x04 - disable OSC
+   *   0x02 - disable analog
+   *   0x01 - disable clock signal
+   */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc4);
+
+  /* Step 16: */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  /* Step 18: */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc3);
+
+  /* Step 19: */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  return OK;
+}
+
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row)
+{
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+  uint8_t *src = priv->shadow_fb + row * SSD1680_DEV_ROWSIZE;
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, row, row >> 8);
+
+#if SSD1680_DEV_BPP == 1
+  ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_RAM1, src,
+      SSD1680_DEV_ROWSIZE);
+#else
+  ssd1680_snd_cmd_with_data_even_bits(priv, SSD1680_WRITE_RAM1, src,
+      SSD1680_DEV_ROWSIZE);
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 1);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT,
+      row, row >> 8);
+  ssd1680_snd_cmd_with_data_odd_bits(priv,
+    SSD1680_WRITE_RAM2, src, SSD1680_DEV_FBSIZE);
+#endif
+
+  if (row == SSD1680_DEV_FB_YRES - 1)
+    {
+      ssd1680_redraw_display(priv);
+    }
+#else
+  int row_group = (row >> 3) << 3;
+  uint8_t *src = priv->shadow_fb + row_group * SSD1680_DEV_ROWSIZE;
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, row >> 3);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+
+#if SSD1680_DEV_BPP == 1
+  ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1, src,
+      SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE);
+#else
+#error "Landscape mode with 3 colors is not implemented"
+  /* TODO send ssd1680_snd_cmd_with_data_even_bits_bitstrip
+   * (priv, SSD1680_WRITE_RAM1, src, SSD1680_DEV_NATIVE_YRES,
+   *  SSD1680_DEV_ROWSIZE);
+   */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, row >> 3);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+
+  /*  TODO send ssd1680_snd_cmd_with_data_odd_bits_bitstrip(priv,
+   *  SSD1680_WRITE_RAM2, src, SSD1680_DEV_NATIVE_YRESSSD1680_DEV_ROWSIZE, );
+   */
+
+#endif
+
+  if (row == SSD1680_DEV_FB_YRES - 1)
+    {
+      ssd1680_redraw_display(priv);
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ssd1680_configspi
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi)
+{
+  /* Configure SPI for the SSD1680 */
+
+  SPI_SETMODE(spi, CONFIG_SSD1680_SPIMODE);
+  SPI_SETBITS(spi, 8);
+  SPI_HWFEATURES(spi, 0);
+  SPI_SETFREQUENCY(spi, CONFIG_SSD1680_FREQUENCY);
+}
+
+/****************************************************************************
+ * Name: ssd1680_select
+ *
+ * Description:
+ *   Enable/Disable SSD1680 SPI CS
+ *
+ ****************************************************************************/
+
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs)
+{
+  /* If we are selecting the device */
+
+  if (cs == true)
+    {
+      /* If SPI bus is shared then lock and configure it */
+
+      SPI_LOCK(priv->spi, true);
+      ssd1680_configspi(priv->spi);
+    }
+
+  /* Select/deselect SPI device */
+
+  SPI_SELECT(priv->spi, SPIDEV_DISPLAY(0), cs);
+
+  /* If we are deselecting the device */
+
+  if (cs == false)

Review Comment:
   `if (!cs)`



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("contrast: %d\n", priv->contrast);
+  return priv->contrast;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setcontrast
+ *
+ * Description:
+ *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+
+  lcdinfo("ignoring set constrast(%d)\n", contrast);
+  DEBUGASSERT(priv);
+
+  /* Verify the contrast value */
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (contrast > CONFIG_LCD_MAXCONTRAST)
+    {
+      return -EINVAL;
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_configuredisplay
+ *
+ * Description:
+ *   Setup LCD display.
+ *
+ ****************************************************************************/
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv)
+{
+  lcdinfo("Start\n");
+
+  /* Software Reset */
+
+  lcdinfo("Software reset: (0x%02x)\n", SSD1680_SW_RESET);
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_SW_RESET);
+
+  /* Busy wait */
+
+  ssd1680_busy_wait(priv);
+  lcdinfo("SSD1680 is ready\n");
+
+  /* Step 1: Driver Output Control 3 bytes of data:
+   * - A[8:0] MUX Gate lines
+   * - B[2:0] sequence
+   * last data byte depends on connection between display and controller
+   */
+
+  lcdinfo("Set the driver output controll (0x%02x): %d 0x%02x\n",
+      SSD1680_DRIVER_CONTROL, SSD1680_DEV_NATIVE_YRES - 1,
+      SSD1680_DEV_GATE_LAYOUT);
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_DRIVER_CONTROL,
+     (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+     (SSD1680_DEV_NATIVE_YRES - 1) >> 8,
+     SSD1680_DEV_GATE_LAYOUT);
+
+  /* Step 2: SSD1680_BOOST_SOFTSTART 0x0C D7 D6 9D */
+
+  lcdinfo("Set boost soft start\n");
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_BOOST_SOFTSTART,
+      0xd7, 0xd6, 0x9d);
+
+  /* Step 3: Vcom Voltage SSD1680_WRITE_VCOM, 0x36 */
+
+  lcdinfo("Set Vcom voltage (0x%02x): 0x%2x\n", SSD1680_WRITE_VCOM, 0xa8);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_WRITE_VCOM, 0xa8);
+
+  /* Step 4: Sending undocumented command: 0x3a with data 1A */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3a, 0x1a);
+  ssd1680_snd_cmd_with_data1(priv, 0x3a, 0x1a);
+
+  /* Step 5: Sending undocumented command: 0x3b with data 08 */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3b, 0x08);
+  ssd1680_snd_cmd_with_data1(priv, 0x3b, 0x08);
+
+  /* Step 6: Data entry mode SSD1680_DATA_MODE, 0x03
+   * TODO w arduino była wartość 0x01
+   */
+
+  lcdinfo("Set data entry mode (0x%02x): 0x%2x\n",
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+  ssd1680_snd_cmd_with_data1(priv,
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+
+  /* Step 7: Set ram X start/end postion 00 FF */
+
+  lcdinfo("Set ram X start/end position (0x%02x): 0, %d\n",
+      SSD1680_SET_RAMXPOS, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMXPOS,
+      0x00, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+
+  /* Step 8: Set ram Y start/end postion
+   * TODO w adruino zamieniona start ze stopem
+   */
+
+  lcdinfo("Set ram Y start/end position (%x): 0, %d\n",
+      SSD1680_SET_RAMYPOS, SSD1680_DEV_NATIVE_YRES - 1);
+  ssd1680_snd_cmd_with_data4(priv, SSD1680_SET_RAMYPOS,
+      0x00, 0x00,
+      (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+      (SSD1680_DEV_NATIVE_YRES - 1) >> 8);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 11: Lookup table */
+
+  lcdinfo("Write lookup table (%d bytes)\n", sizeof (ssd1680_lut));
+  ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_LUT, ssd1680_lut,
+      sizeof (ssd1680_lut));
+
+  /* Step 12: Write sequence */
+
+  lcdinfo("Write controll sequence 0x%02x\n", 0xc0);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc0);
+
+  /* Step 13: Master Activate and busy wait */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  lcdinfo("Configuration ready\n");
+  priv->is_conf = true;
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_redrawfb
+ *
+ * Description:
+ *   Redraw full framebuffer to display
+ *
+ * Input Parameters:
+ *   priv   - Reference to private driver structure
+ *
+ * Assumptions:
+ *   Caller has selected the OLED section.
+ *
+ ****************************************************************************/
+
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv)
+{
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+#if defined(CONFIG_LCD_PORTRAIT) | defined(CONFIG_LCD_RLANDSCAPE)
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+#elif defined (CONFIG_LCD_LANDSCAPE)
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+#elif defined (CONFIG_LCD_RORTRAIT)
+#error "Not implemented LSC orientation"
+#endif
+
+  /* Step 14: */
+
+#if SSD1680_DEV_BPP == 1
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+  ssd1680_snd_cmd_with_data(priv,
+    SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+#else
+  int line;
+#  if SSD1680_DEV_BPP == 1
+  for (line = 0; line < SSD1680_DEV_FB_YRES; line += 8)
+    {
+      ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1,
+          priv->shadow_fb + line * SSD1680_DEV_NATIVE_YRES,
+          SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE);
+    }
+#  else
+#    error "3 color mode not implemented yet"
+#  endif
+#endif
+
+#else
+  ssd1680_snd_cmd_with_data_even_bits(priv,
+    SSD1680_WRITE_RAM1, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 14b: */
+
+  ssd1680_snd_cmd_with_data_odd_bits(priv,
+    SSD1680_WRITE_RAM2, priv->shadow_fb, SSD1680_DEV_FBSIZE);
+#endif
+  ssd1680_redraw_display(priv);
+  return OK;
+}
+
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv)
+{
+  /* Step 15:
+   * Set control register 2.
+   * 1 byte of data with following bits:
+   *   0x80 - enable clock signal
+   *   0x40 - enable analog
+   *   0x20 - load temperature value
+   *   0x10 - Load LUT
+   *   0x08 - Display Mode 2
+   *   0x04 - disable OSC
+   *   0x02 - disable analog
+   *   0x01 - disable clock signal
+   */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc4);
+
+  /* Step 16: */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  /* Step 18: */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc3);
+
+  /* Step 19: */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  return OK;
+}
+
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row)
+{
+#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
+  uint8_t *src = priv->shadow_fb + row * SSD1680_DEV_ROWSIZE;
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, row, row >> 8);
+
+#if SSD1680_DEV_BPP == 1
+  ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_RAM1, src,
+      SSD1680_DEV_ROWSIZE);
+#else
+  ssd1680_snd_cmd_with_data_even_bits(priv, SSD1680_WRITE_RAM1, src,
+      SSD1680_DEV_ROWSIZE);
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 1);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT,
+      row, row >> 8);
+  ssd1680_snd_cmd_with_data_odd_bits(priv,
+    SSD1680_WRITE_RAM2, src, SSD1680_DEV_FBSIZE);
+#endif
+
+  if (row == SSD1680_DEV_FB_YRES - 1)
+    {
+      ssd1680_redraw_display(priv);
+    }
+#else
+  int row_group = (row >> 3) << 3;
+  uint8_t *src = priv->shadow_fb + row_group * SSD1680_DEV_ROWSIZE;
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, row >> 3);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+
+#if SSD1680_DEV_BPP == 1
+  ssd1680_snd_cmd_with_data_bitstrip(priv, SSD1680_WRITE_RAM1, src,
+      SSD1680_DEV_NATIVE_YRES, SSD1680_DEV_ROWSIZE);
+#else
+#error "Landscape mode with 3 colors is not implemented"
+  /* TODO send ssd1680_snd_cmd_with_data_even_bits_bitstrip
+   * (priv, SSD1680_WRITE_RAM1, src, SSD1680_DEV_NATIVE_YRES,
+   *  SSD1680_DEV_ROWSIZE);
+   */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, row >> 3);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0, 0);
+
+  /*  TODO send ssd1680_snd_cmd_with_data_odd_bits_bitstrip(priv,
+   *  SSD1680_WRITE_RAM2, src, SSD1680_DEV_NATIVE_YRESSSD1680_DEV_ROWSIZE, );
+   */
+
+#endif
+
+  if (row == SSD1680_DEV_FB_YRES - 1)
+    {
+      ssd1680_redraw_display(priv);
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ssd1680_configspi
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi)
+{
+  /* Configure SPI for the SSD1680 */
+
+  SPI_SETMODE(spi, CONFIG_SSD1680_SPIMODE);
+  SPI_SETBITS(spi, 8);
+  SPI_HWFEATURES(spi, 0);
+  SPI_SETFREQUENCY(spi, CONFIG_SSD1680_FREQUENCY);
+}
+
+/****************************************************************************
+ * Name: ssd1680_select
+ *
+ * Description:
+ *   Enable/Disable SSD1680 SPI CS
+ *
+ ****************************************************************************/
+
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs)
+{
+  /* If we are selecting the device */
+
+  if (cs == true)

Review Comment:
   `if (cs)`



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("contrast: %d\n", priv->contrast);
+  return priv->contrast;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setcontrast
+ *
+ * Description:
+ *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;

Review Comment:
   ditto



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("contrast: %d\n", priv->contrast);
+  return priv->contrast;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setcontrast
+ *
+ * Description:
+ *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+
+  lcdinfo("ignoring set constrast(%d)\n", contrast);
+  DEBUGASSERT(priv);
+
+  /* Verify the contrast value */
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (contrast > CONFIG_LCD_MAXCONTRAST)
+    {
+      return -EINVAL;
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_configuredisplay
+ *
+ * Description:
+ *   Setup LCD display.
+ *
+ ****************************************************************************/
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv)
+{
+  lcdinfo("Start\n");
+
+  /* Software Reset */
+
+  lcdinfo("Software reset: (0x%02x)\n", SSD1680_SW_RESET);
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_SW_RESET);
+
+  /* Busy wait */
+
+  ssd1680_busy_wait(priv);
+  lcdinfo("SSD1680 is ready\n");
+
+  /* Step 1: Driver Output Control 3 bytes of data:
+   * - A[8:0] MUX Gate lines
+   * - B[2:0] sequence
+   * last data byte depends on connection between display and controller
+   */
+
+  lcdinfo("Set the driver output controll (0x%02x): %d 0x%02x\n",
+      SSD1680_DRIVER_CONTROL, SSD1680_DEV_NATIVE_YRES - 1,
+      SSD1680_DEV_GATE_LAYOUT);
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_DRIVER_CONTROL,
+     (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+     (SSD1680_DEV_NATIVE_YRES - 1) >> 8,
+     SSD1680_DEV_GATE_LAYOUT);
+
+  /* Step 2: SSD1680_BOOST_SOFTSTART 0x0C D7 D6 9D */
+
+  lcdinfo("Set boost soft start\n");
+  ssd1680_snd_cmd_with_data3(priv, SSD1680_BOOST_SOFTSTART,
+      0xd7, 0xd6, 0x9d);
+
+  /* Step 3: Vcom Voltage SSD1680_WRITE_VCOM, 0x36 */
+
+  lcdinfo("Set Vcom voltage (0x%02x): 0x%2x\n", SSD1680_WRITE_VCOM, 0xa8);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_WRITE_VCOM, 0xa8);
+
+  /* Step 4: Sending undocumented command: 0x3a with data 1A */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3a, 0x1a);
+  ssd1680_snd_cmd_with_data1(priv, 0x3a, 0x1a);
+
+  /* Step 5: Sending undocumented command: 0x3b with data 08 */
+
+  lcdinfo("Set (0x%02x): 0x%2x\n", 0x3b, 0x08);
+  ssd1680_snd_cmd_with_data1(priv, 0x3b, 0x08);
+
+  /* Step 6: Data entry mode SSD1680_DATA_MODE, 0x03
+   * TODO w arduino była wartość 0x01
+   */
+
+  lcdinfo("Set data entry mode (0x%02x): 0x%2x\n",
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+  ssd1680_snd_cmd_with_data1(priv,
+      SSD1680_DATA_MODE, SSD1680_VAL_DATA_MODE);
+
+  /* Step 7: Set ram X start/end postion 00 FF */
+
+  lcdinfo("Set ram X start/end position (0x%02x): 0, %d\n",
+      SSD1680_SET_RAMXPOS, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMXPOS,
+      0x00, (SSD1680_DEV_X_ROUND_UP >> 3)-1);
+
+  /* Step 8: Set ram Y start/end postion
+   * TODO w adruino zamieniona start ze stopem
+   */
+
+  lcdinfo("Set ram Y start/end position (%x): 0, %d\n",
+      SSD1680_SET_RAMYPOS, SSD1680_DEV_NATIVE_YRES - 1);
+  ssd1680_snd_cmd_with_data4(priv, SSD1680_SET_RAMYPOS,
+      0x00, 0x00,
+      (uint8_t)((SSD1680_DEV_NATIVE_YRES - 1) & 0xff),
+      (SSD1680_DEV_NATIVE_YRES - 1) >> 8);
+
+  /* Step 9: SSD1680_SET_RAMXCOUNT, 0 */
+
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_SET_RAMXCOUNT, 0x00);
+
+  /* Step 10: SSD1680_SET_RAMYCOUNT, 0, 0 */
+
+  ssd1680_snd_cmd_with_data2(priv, SSD1680_SET_RAMYCOUNT, 0x00, 0x00);
+
+  /* Step 11: Lookup table */
+
+  lcdinfo("Write lookup table (%d bytes)\n", sizeof (ssd1680_lut));
+  ssd1680_snd_cmd_with_data(priv, SSD1680_WRITE_LUT, ssd1680_lut,
+      sizeof (ssd1680_lut));
+
+  /* Step 12: Write sequence */
+
+  lcdinfo("Write controll sequence 0x%02x\n", 0xc0);
+  ssd1680_snd_cmd_with_data1(priv, SSD1680_DISP_CTRL2, 0xc0);
+
+  /* Step 13: Master Activate and busy wait */
+
+  ssd1680_snd_cmd_with_data0(priv, SSD1680_MASTER_ACTIVATE);
+  ssd1680_busy_wait(priv);
+
+  lcdinfo("Configuration ready\n");
+  priv->is_conf = true;
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_redrawfb
+ *
+ * Description:
+ *   Redraw full framebuffer to display
+ *
+ * Input Parameters:
+ *   priv   - Reference to private driver structure
+ *
+ * Assumptions:
+ *   Caller has selected the OLED section.
+ *
+ ****************************************************************************/
+
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv)

Review Comment:
   ditto



##########
drivers/lcd/ssd1680.c:
##########
@@ -0,0 +1,1382 @@
+/****************************************************************************
+ * drivers/lcd/ssd1680.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.
+ *
+ ****************************************************************************/
+
+/* Driver for e-paper displays with SSD1680 controller.
+ *
+ * References:
+ *   1. https://www.crystalfontz.com/controllers/SolomonSystech/SSD1680/497/
+ *   2. https://www.adafruit.com/product/4947
+ *   3. https://github.com/adamkaliszan/TTGO-Electronic-Badge
+
+ * TTGO-Electronic-Badge sequence that display picture:
+ *      cmd:      dta:
+ * Hardware reset and busy wait
+ *   1)   0x01      27 01 00
+ *   2)   0x0C      D7 D6 9D         boost soft start
+ *   3)   0x2c      A8               write VCom
+ *   4)   0x3A      1A           ??? Can't find it in in SSD documentation
+ *   5)   0x3B      08           ??? Can't find it in in SSD documentation
+ *   6)   0x11      01               Data Mode
+ *   7)   0x44      00 0F
+ *   8)   0x45      27 01 00 00
+ *   9)   0x4E      00
+ *   10)  0x4F      27 01
+ *   11)  0x32      50 AA 55 AA 11 00 00 00 00 00 00 00 00 00 00 00
+ *                  00 00 00 00 FF FF 1F 00 00 00 00 00 00 00
+ *   12)  0x22      C0
+ *   13)  0x20
+ * Busy Wait
+ *   14)  0x24      0xFF ... 4736 bytes with bitmap
+ *   15)  0x22      C4
+ *   16)  0x20
+ * Busy Wait
+ *   17)  0xFF
+ *   18)  0x22      C3
+ *   19)  0x20
+ * Busy Wait
+ */
+
+/****************************************************************************
+ * Device memory organization:
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/arch.h>
+#include <nuttx/spi/spi.h>
+#include <nuttx/lcd/lcd.h>
+#include <nuttx/lcd/ssd1680.h>
+#include <nuttx/signal.h>
+
+#include <arch/irq.h>
+
+#include "ssd1680.h"
+
+#ifdef CONFIG_LCD_SSD1680
+/* This structure describes the state of the SSD1680 driver */
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct ssd1680_dev_s
+{
+  struct lcd_dev_s       dev;      /* Publicly visible device structure */
+
+  /* Private LCD-specific information follows */
+
+  FAR struct spi_dev_s  *spi;      /* Cached SPI device reference */
+  uint8_t                contrast; /* Current contrast setting */
+  bool                   on;       /* true: display is on */
+  bool                   is_conf;  /* true: display had been configured */
+
+  FAR const struct ssd1680_priv_s *board_priv; /* Board specific structure */
+
+  /* The SSD1680 does not support reading from the display memory in SPI
+   * mode. Since there is 1 BPP and access is byte-by-byte.
+   * Also shared line (MISO/MOSI) could be problematic. Now implementation
+   * uses shadow buffer.
+   * Its size depends on resolution and is between 4kB and 32 kB.
+   */
+
+  uint8_t shadow_fb[SSD1680_DEV_FBSIZE];
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Libc extension */
+
+FAR void *bitscpy_ds(FAR void *dest, int dest_offset, FAR const void *src,
+    size_t nbits);
+
+FAR void *bitscpy_ss(FAR void *dest, FAR const void *src, int src_offset,
+    size_t nbits);
+
+/* LCD Data Transfer Methods */
+
+static void ssd1680_busy_wait(FAR struct ssd1680_dev_s *priv);
+
+static void ssd1680_snd_cmd_with_data0(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd);
+
+static void ssd1680_snd_cmd_with_data1(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1);
+
+static void ssd1680_snd_cmd_with_data2(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2);
+
+static void ssd1680_snd_cmd_with_data3(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3);
+
+static void ssd1680_snd_cmd_with_data4(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, uint8_t dta1, uint8_t dta2, uint8_t dta3, uint8_t dta4);
+
+static void ssd1680_snd_cmd_with_data(FAR struct ssd1680_dev_s *priv,
+    uint8_t cmd, const uint8_t *dta, int dta_len);
+
+#if !defined(CONFIG_LCD_PORTRAIT) && !defined(CONFIG_LCD_RPORTRAIT)
+#  if SSD1680_DEV_BPP == 1
+static void ssd1680_snd_cmd_with_data_bitstrip(
+    FAR struct ssd1680_dev_s *priv, uint8_t cmd, const uint8_t *dta,
+    int dta_len, int strip_len);
+#  else
+/* Special functions for sending to RAM1 and RAM2 should be implemented
+ * ssd1680_snd_cmd_with_data_bitstrip works fine with 1 bit per pixel
+ */
+#    error "SSD1680 driver has no implementation for 3 color with landscape"
+#  endif
+#endif
+
+static void ssd1680_configspi(FAR struct spi_dev_s *spi);
+static void ssd1680_select(FAR struct ssd1680_dev_s *priv, bool cs);
+static void ssd1680_cmddata(FAR struct ssd1680_dev_s *priv, bool cmd);
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels);
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels);
+
+/* LCD Configuration */
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo);
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo);
+
+/* LCD RGB Mapping */
+
+#ifdef CONFIG_FB_CMAP
+#  error "RGB color mapping not supported by this driver"
+#endif
+
+/* Cursor Controls */
+
+#ifdef CONFIG_FB_HWCURSOR
+#  error "Cursor control not supported by this driver"
+#endif
+
+/* LCD Specific Controls */
+
+static int ssd1680_getpower(struct lcd_dev_s *dev);
+static int ssd1680_setpower(struct lcd_dev_s *dev, int power);
+static int ssd1680_getcontrast(struct lcd_dev_s *dev);
+static int ssd1680_setcontrast(struct lcd_dev_s *dev,
+                               unsigned int contrast);
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv);
+static int ssd1680_redraw_display(struct ssd1680_dev_s *priv);
+static int ssd1680_redrawfb_row(struct ssd1680_dev_s *priv, int row);
+static int ssd1680_redrawfb(struct ssd1680_dev_s *priv);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* This is working memory allocated by the LCD driver for each LCD device
+ * and for each color plane.  This memory will hold one raster line of data.
+ * The size of the allocated run buffer must therefore be at least
+ * (bpp * xres / 8).  Actual alignment of the buffer must conform to the
+ * bitwidth of the underlying pixel type.
+ *
+ * If there are multiple planes, they may share the same working buffer
+ * because different planes will not be operate on concurrently.  However,
+ * if there are multiple LCD devices, they must each have unique run buffers.
+ */
+
+static uint8_t g_runbuffer[SSD1680_DEV_ROWSIZE];
+
+/* This structure describes the overall LCD video controller */
+
+static const struct fb_videoinfo_s g_videoinfo =
+{
+  .fmt     = SSD1680_DEV_COLORFMT,    /* Color format: B&W */
+  .xres    = SSD1680_DEV_FB_XRES,     /* Horizontal resolution in pixel columns */
+  .yres    = SSD1680_DEV_FB_YRES,     /* Vertical resolution in pixel rows */
+  .nplanes = SSD1680_NO_OF_PLANES,    /* Number of color planes supported */
+};
+
+/* This is the standard, NuttX Plane information object */
+
+static const struct lcd_planeinfo_s g_planeinfo =
+{
+  .putrun  = ssd1680_putrun,             /* Put a run into LCD memory */
+  .putarea = NULL,                       /* Not need to implement */
+  .getrun  = ssd1680_getrun,             /* Read content of shadow memory */
+  .getarea = NULL,                       /* Not need to implement */
+  .buffer  = (FAR uint8_t *)g_runbuffer, /* Run scratch buffer */
+  .bpp     = SSD1680_DEV_BPP,            /* Bits-per-pixel */
+};
+
+/* This is the outside visible interface for the OLED driver */
+
+static const struct lcd_dev_s g_lcd_epaper_dev =
+{
+  /* LCD Configuration */
+
+  .getvideoinfo = ssd1680_getvideoinfo,
+  .getplaneinfo = ssd1680_getplaneinfo,
+
+  /* LCD RGB Mapping -- Not supported */
+
+  /* Cursor Controls -- Not supported */
+
+  /* LCD Specific Controls */
+
+  .getpower     = ssd1680_getpower,
+  .setpower     = ssd1680_setpower,
+  .getcontrast  = ssd1680_getcontrast,
+  .setcontrast  = ssd1680_setcontrast,
+};
+
+/* This is the OLED driver instance. Only a single device is supported
+ * for now.
+ */
+
+static struct ssd1680_dev_s g_epaperdev;
+
+static const uint8_t ssd1680_lut[] =
+{
+  0x50, 0xaa, 0x55, 0xaa, 0x11, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name:  ssd1680_putrun
+ *
+ * Description:
+ *   This method can be used to write a partial raster line to the LCD.
+ *
+ * Input Parameters:
+ *   row     - Starting row to write to (range: 0 <= row < yres)
+ *   col     - Starting column to write to (range: 0 <= col <= xres-npixels)
+ *   buffer  - The buffer containing the run to be written to the LCD
+ *   npixels - The number of pixels to write to the LCD
+ *             (range: 0 < npixels <= xres-col)
+ *
+ ****************************************************************************/
+
+static int ssd1680_putrun(fb_coord_t row, fb_coord_t col,
+    FAR const uint8_t *buffer, size_t npixels)
+{
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  uint8_t *dst = priv->shadow_fb +
+      row * SSD1680_DEV_ROWSIZE + (col >> SSD1680_PDF);
+
+  int dst_start_bitshift = col % (SSD1680_PDV);
+
+  /* Write data to shadow memory */
+
+  bitscpy_ds(dst,  dst_start_bitshift, buffer, npixels);
+
+  /* Redraw Screen */
+
+  ssd1680_redrawfb_row(priv, row);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getrun
+ *
+ * Description:
+ *   This method can be used to read a partial raster line from the LCD:
+ *
+ * Input Parameters:
+ *
+ *  row     - Starting row to read from (range: 0 <= row < yres)
+ *  col     - Starting column to read read
+ *            (range: 0 <= col <= xres-npixels)
+ *  buffer  - The buffer in which to return the run read from the LCD
+ *  npixels - The number of pixels to read from the LCD
+ *            (range: 0 < npixels <= xres-col)
+ */
+
+static int ssd1680_getrun(fb_coord_t row, fb_coord_t col,
+    FAR uint8_t *buffer, size_t npixels)
+{
+  lcdinfo("(%d, %d, %d)\n", row, col, npixels);
+  FAR struct ssd1680_dev_s *priv = (FAR struct ssd1680_dev_s *)&g_epaperdev;
+
+  bitscpy_ss(buffer,
+      priv->shadow_fb + row * SSD1680_DEV_FBSIZE + (col >> SSD1680_PDF),
+      col % SSD1680_PDV,
+      npixels);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getvideoinfo
+ *
+ * Description:
+ *   Get information about the LCD video controller configuration.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getvideoinfo(FAR struct lcd_dev_s *dev,
+                                FAR struct fb_videoinfo_s *vinfo)
+{
+  DEBUGASSERT(dev && vinfo);
+  lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
+          g_videoinfo.fmt, g_videoinfo.xres, g_videoinfo.yres,
+          g_videoinfo.nplanes);
+  memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getplaneinfo
+ *
+ * Description:
+ *   Get information about the configuration of each LCD color plane.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getplaneinfo(FAR struct lcd_dev_s *dev,
+                                unsigned int planeno,
+                                FAR struct lcd_planeinfo_s *pinfo)
+{
+  DEBUGASSERT(pinfo && planeno == 0);
+
+  lcdinfo("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
+  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getpower
+ *
+ * Description:
+ *   Get the LCD panel power status:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_getpower(FAR struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("power: %s\n", priv->on ? "ON" : "OFF");
+  return priv->on ? CONFIG_LCD_MAXPOWER : 0;
+}
+
+static void ssd1680_reset(struct ssd1680_dev_s *priv)
+{
+  if (priv->board_priv && priv->board_priv->set_rst)
+    {
+      lcdinfo("Hardware reset\n");
+      priv->board_priv->set_rst(false);
+      nxsig_usleep(10);
+      priv->board_priv->set_rst(true);
+    }
+  else
+    {
+      lcdinfo("Hardware reset is not available. Operation skipped.\n");
+    }
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setpower
+ *
+ * Description:
+ *   Enable/disable LCD panel power:
+ *     0: full off
+ *     CONFIG_LCD_MAXPOWER: full on
+ *   On backlit LCDs, this setting may correspond to the backlight setting.
+ *
+ ****************************************************************************/
+
+static int ssd1680_setpower(FAR struct lcd_dev_s *dev, int power)
+{
+  int ret = OK;
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *) dev;
+  DEBUGASSERT(dev);
+  lcdinfo("power: %d -> %d\n", priv->on ? CONFIG_LCD_MAXPOWER : 0, power);
+  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);
+
+  if (power <= 0)
+    {
+      priv->on = false;
+
+      /* Try turn off power completely */
+
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power off. */
+
+          if (priv->board_priv->set_vcc(false))
+            {
+              /* Display is completely powered off, not configured anymore. */
+
+            priv->is_conf = false;
+            }
+        }
+    }
+  else
+    {
+      if (priv->board_priv && priv->board_priv->set_vcc)
+        {
+          /* Do power on. */
+
+          lcdinfo("Set Pwr Ctrl Linepower ON\n");
+          priv->board_priv->set_vcc(true);
+          nxsig_usleep(10000);
+        }
+      else
+        {
+          lcdinfo("No line for controlling PWR, operation skipped\n");
+        }
+
+      if (!priv->is_conf)
+        {
+          ssd1680_reset(priv);
+
+          ret = ssd1680_configuredisplay(priv);
+          if (ret < 0)
+            {
+              return ret;
+            }
+
+          /* Draw the framebuffer */
+
+          ret = ssd1680_redrawfb(priv);
+        }
+
+      if (ret < 0)
+        {
+          return ret;
+        }
+
+      priv->on = true;
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_getcontrast
+ *
+ * Description:
+ *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_getcontrast(struct lcd_dev_s *dev)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+  DEBUGASSERT(priv);
+
+  lcdinfo("contrast: %d\n", priv->contrast);
+  return priv->contrast;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_setcontrast
+ *
+ * Description:
+ *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
+ *
+ ****************************************************************************/
+
+static int ssd1680_setcontrast(struct lcd_dev_s *dev, unsigned int contrast)
+{
+  struct ssd1680_dev_s *priv = (struct ssd1680_dev_s *)dev;
+
+  lcdinfo("ignoring set constrast(%d)\n", contrast);
+  DEBUGASSERT(priv);
+
+  /* Verify the contrast value */
+
+#ifdef CONFIG_DEBUG_FEATURES
+  if (contrast > CONFIG_LCD_MAXCONTRAST)
+    {
+      return -EINVAL;
+    }
+#endif
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name:  ssd1680_configuredisplay
+ *
+ * Description:
+ *   Setup LCD display.
+ *
+ ****************************************************************************/
+
+static int ssd1680_configuredisplay(struct ssd1680_dev_s *priv)

Review Comment:
   ditto



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org