/****************************************************************************
 * drivers/lcd/ra8875.c
 *
 * Driver for the RAiO Technologies RA8875 LCD controller
 *
 *   Copyright (C) 2015 Intuitive Aerial AB. All rights reserved.
 *   Author: Marten Svanfeldt <marten@intuitiveaerial.com>
 *
 * References: RA8875, Rev 1.6, Apr 2013, RAiO Technologies Inc
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <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/ra8875.h>

#include "ra8875.h"

#ifdef CONFIG_LCD_RA8875

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/* Configuration ************************************************************/

/* Check contrast selection */

#if !defined(CONFIG_LCD_MAXCONTRAST)
#  define CONFIG_LCD_MAXCONTRAST 1
#endif

/* Check power setting */

#if !defined(CONFIG_LCD_MAXPOWER) || CONFIG_LCD_MAXPOWER < 1
#  define CONFIG_LCD_MAXPOWER 1
#endif

#if CONFIG_LCD_MAXPOWER > 255
#  error "CONFIG_LCD_MAXPOWER must be less than 256 to fit in uint8_t"
#endif

/* Check orientation */

#if defined(CONFIG_LCD_PORTRAIT)
#  if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE) || \
      defined(CONFIG_LCD_RPORTRAIT)
#    error "Cannot define both portrait and any other orientations"
#  endif
#elif defined(CONFIG_LCD_RPORTRAIT)
#  if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
#    error "Cannot define both rportrait and any other orientations"
#  endif
#elif defined(CONFIG_LCD_LANDSCAPE)
#  ifdef CONFIG_LCD_RLANDSCAPE
#    error "Cannot define both landscape and any other orientations"
#  endif
#elif !defined(CONFIG_LCD_RLANDSCAPE)
#  define CONFIG_LCD_LANDSCAPE 1
#endif

/* Display/Color Properties *************************************************/

/* Display Resolution */

#if defined(CONFIG_RA8875_XRES)
#  define RA8875_XRES       CONFIG_RA8875_XRES
#else
#  if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
#    define RA8875_XRES       800
#  else
#    define RA8875_XRES       480
#  endif
#endif

#if defined(CONFIG_RA8875_YRES)
#  define RA8875_YRES       CONFIG_RA8875_YRES
#else
#  if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
#    define RA8875_YRES       480
#  else
#    define RA8875_YRES       800
#  endif
#endif

#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
#  define RA8875_HW_XRES RA8875_XRES
#  define RA8875_HW_YRES RA8875_YRES
#else
#  define RA8875_HW_XRES RA8875_YRES
#  define RA8875_HW_YRES RA8875_XRES
#endif

/* Color depth and format */

#if defined(CONFIG_LCD_RA8875_65K)
#  define RA8875_BPP           16
#  define RA8875_COLORFMT      FB_FMT_RGB16_565

#  define RA8875_UNPACK_RED(p)    (((p)>>11) & 0x1f)
#  define RA8875_UNPACK_GREEN(p)  (((p)>>5) & 0x3f)
#  define RA8875_UNPACK_BLUE(p)   ((p) & 0x1f)

#  define RA8875_PACK_RGB(r,g,b) ((r) << 11 | (g) << 5 | (b))
#else /* LCD_RA8875_256 */
#  define RA8875_BPP           8
#  define RA8875_COLORFMT      FB_FMT_RGB8_332

#  define RA8875_UNPACK_RED(p)    (((p)>>5) & 0x7)
#  define RA8875_UNPACK_GREEN(p)  (((p)>>2) & 0x7)
#  define RA8875_UNPACK_BLUE(p)   ((p) & 0x3)

#  define RA8875_PACK_RGB(r,g,b) (((r)&0x7) << 5 | ((g)&0x7) << 2 | ((b)&0x3))
#endif

#if RA8875_HW_XRES >= 800 && RA8875_HW_YRES >= 480 && RA8875_BPP > 8
#  undef RA8875_2LAYER_POSSIBLE
#else
#  define RA8875_2LAYER_POSSIBLE 1
#endif

/****************************************************************************
 * Private Type Definition
 ****************************************************************************/

/* This structure describes the state of this driver */

struct ra8875_dev_s
{
  /* Publicly visible device structure */

  struct lcd_dev_s dev;

  /* Private LCD-specific information follows */

  FAR struct ra8875_lcd_s *lcd;   /* The contained platform-specific, LCD interface */
  uint8_t power;                  /* Current power setting */

#if defined(RA8875_2LAYER_POSSIBLE)
  uint8_t current_layer;          /* Current drawing layer, 0=disabled */
#endif

  /* Shadow these registers to speed up rendering */

  uint8_t shadow_mwcr0;
  uint16_t shadow_w_curh;
  uint16_t shadow_w_curv;

  /* These fields simplify and reduce debug output */

#ifdef CONFIG_DEBUG_LCD
  bool put;                       /* Last raster operation was a putrun */
  fb_coord_t firstrow;            /* First row of the run */
  fb_coord_t lastrow;             /* Last row of the run */
  fb_coord_t col;                 /* Column of the run */
  size_t npixels;                 /* Length of the run */
#endif

  /* 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.
   */

  uint16_t runbuffer[RA8875_XRES];
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* Low Level LCD access */

static inline void ra8875_putreg(FAR struct ra8875_lcd_s *lcd,
                                 uint8_t regaddr,
                                 uint8_t regval);
static inline void ra8875_putreg16(FAR struct ra8875_lcd_s *lcd,
                                   uint8_t regaddr,
                                   uint16_t regval);
#ifndef CONFIG_LCD_NOGETRUN
static inline uint8_t ra8875_readreg(FAR struct ra8875_lcd_s *lcd,
                                     uint8_t regaddr);
static inline void ra8875_waitreg(FAR struct ra8875_lcd_s *lcd,
                                  uint8_t regaddr, uint8_t mask);
#endif
static void ra8875_set_writecursor(FAR struct ra8875_dev_s *dev,
                                   uint16_t column, uint16_t row);
static void ra8875_set_readcursor(FAR struct ra8875_lcd_s *lcd,
                                  uint16_t column, uint16_t row);
static void ra8875_set_mwcr0(FAR struct ra8875_dev_s *dev, uint8_t value);
static void ra8875_setwindow(FAR struct ra8875_lcd_s *lcd,
                             uint16_t x, uint16_t y,
                             uint16_t width, uint16_t height);
static inline void ra8875_setbackground(FAR struct ra8875_lcd_s *lcd,
                                        uint16_t color);
static inline void ra8875_setforeground(FAR struct ra8875_lcd_s *lcd,
                                        uint16_t color);
static void ra8875_clearmem(FAR struct ra8875_lcd_s *lcd);

/* LCD Data Transfer Methods */

#if 0 /* Sometimes useful */
static void ra8875_dumprun(FAR const char *msg,
                           FAR uint16_t *run,
                           size_t npixels);
#else
#  define ra8875_dumprun(m,r,n)
#endif

#ifdef CONFIG_DEBUG_LCD
static void ra8875_showrun(FAR struct ra8875_dev_s *priv, fb_coord_t row,
                            fb_coord_t col, size_t npixels, bool put);
#else
#  define ra8875_showrun(p,r,c,n,b)
#endif

static int ra8875_putrun(FAR struct lcd_dev_s *dev,
                         fb_coord_t row, fb_coord_t col,
                         FAR const uint8_t *buffer,
                         size_t npixels);
static int ra8875_getrun(FAR struct lcd_dev_s *dev,
                         fb_coord_t row, fb_coord_t col,
                         FAR uint8_t *buffer,
                         size_t npixels);

/* LCD Configuration */

static int ra8875_getvideoinfo(FAR struct lcd_dev_s *dev,
                               FAR struct fb_videoinfo_s *vinfo);
static int ra8875_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 ra8875_getpower(FAR struct lcd_dev_s *dev);
static int ra8875_setpower(FAR struct lcd_dev_s *dev, int power);
static int ra8875_getcontrast(FAR struct lcd_dev_s *dev);
static int ra8875_setcontrast(FAR struct lcd_dev_s *dev,
                              unsigned int contrast);

/* Initialization */

static inline int ra8875_hwinitialize(FAR struct ra8875_dev_s *priv);

/****************************************************************************
 * Private Data
 ****************************************************************************/

/* This driver can support only a signal RA8875 device.
 * The following is the single RA8875 driver state instance:
 */

static struct ra8875_dev_s g_lcddev;

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name:  ra8875_putreg(lcd,
 *
 * Description:
 *   Write to an LCD register
 *
 ****************************************************************************/

static void ra8875_putreg(FAR struct ra8875_lcd_s *lcd, uint8_t regaddr,
                          uint8_t regval)
{
  /* Set the index register to the register address and write the register
   * contents
   */

  lcdinfo("putreg 0x%02x = 0x%02x\n", regaddr, regval);

  lcd->write_reg(lcd, regaddr, regval);
}

/****************************************************************************
 * Name:  ra8875_putreg16(lcd,
 *
 * Description:
 *   Write to an LCD register
 *
 ****************************************************************************/

static void ra8875_putreg16(FAR struct ra8875_lcd_s *lcd, uint8_t regaddr,
                            uint16_t regval)
{
  /* Set the index register to the register address and write the register
   * contents
   */

  lcdinfo("putreg 0x%02x = 0x%04x\n", regaddr, regval);

  lcd->write_reg16(lcd, regaddr, regval);
}

/****************************************************************************
 * Name:  ra8875_readreg
 *
 * Description:
 *   Read from an LCD register
 *
 ****************************************************************************/

#ifndef CONFIG_LCD_NOGETRUN
static uint8_t ra8875_readreg(FAR struct ra8875_lcd_s *lcd, uint8_t regaddr)
{
  uint8_t regval;
  /* Set the index register to the register address and read the register
   * contents
   */

  regval = lcd->read_reg(lcd, regaddr);

  lcdinfo("readreg 0x%02x = 0x%02x\n", regaddr, regval);
  return regval;
}
#endif

/****************************************************************************
 * Name:  ra8875_waitreg
 *
 * Description:
 *   Wait while an LCD register match set mask
 *
 ****************************************************************************/

#ifndef CONFIG_LCD_NOGETRUN
static void ra8875_waitreg(FAR struct ra8875_lcd_s *lcd,
                           uint8_t regaddr, uint8_t mask)
{
  int i = 20000 / 100;

  while (i-- && ra8875_readreg(lcd, regaddr) & mask)
    {
      up_udelay(100);
    }
}
#endif

/****************************************************************************
 * Name:  ra8875_set_writecursor
 *
 * Description:
 *   Set the position to use for the write cursor
 *
 ****************************************************************************/

static void ra8875_set_writecursor(FAR struct ra8875_dev_s *dev,
                                   uint16_t column,
                                   uint16_t row)
{
  FAR struct ra8875_lcd_s *lcd = dev->lcd;

#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
  if (dev->shadow_w_curh != row)
    {
      ra8875_putreg16(lcd, RA8875_CURH0, row);
      dev->shadow_w_curh = row;
    }

  if (dev->shadow_w_curv != column)
    {
      ra8875_putreg16(lcd, RA8875_CURV0, column);
      dev->shadow_w_curv = column;
    }
#elif defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
  if (dev->shadow_w_curh != column)
    {
      ra8875_putreg16(lcd, RA8875_CURH0, column);
      dev->shadow_w_curh = column;
    }

  if (dev->shadow_w_curv != row)
    {
      ra8875_putreg16(lcd, RA8875_CURV0, row);
      dev->shadow_w_curv = row;
    }
#endif
}

/****************************************************************************
 * Name:  ra8875_set_readcursor
 *
 * Description:
 *   Set the position to use for the read cursor
 *
 ****************************************************************************/

static void ra8875_set_readcursor(FAR struct ra8875_lcd_s *lcd,
                                  uint16_t column,
                                  uint16_t row)
{
#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
  ra8875_putreg16(lcd, RA8875_RCURH0, row);
  ra8875_putreg16(lcd, RA8875_RCURV0, column);
#elif defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
  ra8875_putreg16(lcd, RA8875_RCURH0, column);
  ra8875_putreg16(lcd, RA8875_RCURV0, row);
#endif
}

static void ra8875_set_mwcr0(FAR struct ra8875_dev_s *dev, uint8_t value)
{
  FAR struct ra8875_lcd_s *lcd = dev->lcd;

  if (dev->shadow_mwcr0 != value)
    {
      ra8875_putreg(lcd, RA8875_MWCR0, value);
      dev->shadow_mwcr0 = value;
    }
}

/****************************************************************************
 * Name:  ra8875_setwindow
 *
 * Description:
 *   Set hardware clipping window
 *
 ****************************************************************************/

static void ra8875_setwindow(FAR struct ra8875_lcd_s *lcd,
                             uint16_t x, uint16_t y,
                             uint16_t width, uint16_t height)
{
#if defined(CONFIG_LCD_PORTRAIT) || defined(CONFIG_LCD_RPORTRAIT)
  ra8875_putreg16(lcd, RA8875_HSAW0, y);
  ra8875_putreg16(lcd, RA8875_VSAW0, x);
  ra8875_putreg16(lcd, RA8875_HEAW0, (y + height - 1));
  ra8875_putreg16(lcd, RA8875_VEAW0, (x + width - 1));
#elif defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
  ra8875_putreg16(lcd, RA8875_HSAW0, x);
  ra8875_putreg16(lcd, RA8875_VSAW0, y);
  ra8875_putreg16(lcd, RA8875_HEAW0, (x + width - 1));
  ra8875_putreg16(lcd, RA8875_VEAW0, (y + height - 1));
#endif
}

/****************************************************************************
 * Name:  ra8875_setbackground
 *
 * Description:
 *   Set the background color to use for the BTE engine
 *
 ****************************************************************************/

static inline void ra8875_setbackground(FAR struct ra8875_lcd_s *lcd,
                                        uint16_t color)
{
  ra8875_putreg(lcd, RA8875_BGCR0, RA8875_UNPACK_RED(color));
  ra8875_putreg(lcd, RA8875_BGCR1, RA8875_UNPACK_GREEN(color));
  ra8875_putreg(lcd, RA8875_BGCR2, RA8875_UNPACK_BLUE(color));
}

/****************************************************************************
 * Name:  ra8875_setforeground
 *
 * Description:
 *   Set the foreground color to use for the BTE engine
 *
 ****************************************************************************/

static inline void ra8875_setforeground(FAR struct ra8875_lcd_s *lcd,
                                        uint16_t color)
{
  ra8875_putreg(lcd, RA8875_FGCR0, RA8875_UNPACK_RED(color));
  ra8875_putreg(lcd, RA8875_FGCR1, RA8875_UNPACK_GREEN(color));
  ra8875_putreg(lcd, RA8875_FGCR2, RA8875_UNPACK_BLUE(color));
}

/****************************************************************************
 * Name:  ra8875_clearmem
 *
 * Description:
 *
 *
 ****************************************************************************/

static void ra8875_clearmem(FAR struct ra8875_lcd_s *lcd)
{
  lcdinfo("clearmem start\n");
  ra8875_putreg(lcd, RA8875_MCLR, RA8875_MCLR_CLEAR | RA8875_MCLR_FULL);

  /* Wait for operation to finish */

  ra8875_waitreg(lcd, RA8875_MCLR, RA8875_MCLR_CLEAR);
  lcdinfo("clearmem done\n");
}

/****************************************************************************
 * Name:  ra8875_showrun
 *
 * Description:
 *   When LCD debug is enabled, try to reduce then amount of output data
 *   generated by ra8875_putrun and ra8875_getrun
 *
 ****************************************************************************/

#ifdef CONFIG_DEBUG_LCD
static void ra8875_showrun(FAR struct ra8875_dev_s *priv, fb_coord_t row,
                            fb_coord_t col, size_t npixels, bool put)
{
  fb_coord_t nextrow = priv->lastrow + 1;

  /* Has anything changed
   * (other than the row is the next row in the sequence)?
   */

  if (put == priv->put && row == nextrow && col == priv->col &&
      npixels == priv->npixels)
    {
      /* No, just update the last row */

      priv->lastrow = nextrow;
    }
  else
    {
      /* Yes... then this is the end of the preceding sequence.
       *  Output the last run
       * (if there were more than one run in the sequence).
       */

      if (priv->firstrow != priv->lastrow)
        {
          lcdinfo("...\n");
          lcdinfo("%s row: %d col: %d npixels: %d\n",
                  priv->put ? "PUT" : "GET",
                  priv->lastrow, priv->col, priv->npixels);
        }

      /* And we are starting a new sequence.  Output the first run of the
       * new sequence
       */

      lcdinfo("%s row: %d col: %d npixels: %d\n",
              put ? "PUT" : "GET", row, col, npixels);

      /* And save information about the run so that we can detect
       * continuations of the sequence.
       */

      priv->put      = put;
      priv->firstrow = row;
      priv->lastrow  = row;
      priv->col      = col;
      priv->npixels  = npixels;
    }
}
#endif

/****************************************************************************
 * Name:  ra8875_putrun
 *
 * Description:
 *   This method can be used to write a partial raster line to the LCD:
 *
 *   dev     - The lcd device
 *   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 ra8875_putrun(FAR struct lcd_dev_s *dev,
                         fb_coord_t row, fb_coord_t col,
                         FAR const uint8_t *buffer,
                         size_t npixels)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd = priv->lcd;
  int16_t curhinc = 0;
  int16_t curvinc = 0;

#if RA8875_BPP == 16
  DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);

  FAR const uint16_t *src = (FAR const uint16_t *)buffer;
#else
  FAR const uint8_t *src = (FAR const uint8_t *)buffer;
#endif
  int i;

  /* Buffer must be provided and aligned to a 16-bit address boundary */

  ra8875_showrun(priv, row, col, npixels, true);

#ifdef CONFIG_LCD_LANDSCAPE

  /* Set the cursor position */

  ra8875_set_mwcr0(priv, RA8875_MWCR0_MODE_GRAPHICS |
                         RA8875_MWCR0_MEMDIR_LEFTRIGHT |
                         RA8875_MWCR0_WINC_ENABLE);
  ra8875_set_writecursor(priv, col, row);

  curhinc = npixels;
  curvinc = 0;

#elif defined(CONFIG_LCD_RLANDSCAPE)

  /* Retransform coordinates, write right to left */

  col = (RA8875_XRES - 1) - col;
  row = (RA8875_YRES - 1) - row;

  /* Set the cursor position */

  ra8875_set_mwcr0(priv, RA8875_MWCR0_MODE_GRAPHICS |
                         RA8875_MWCR0_MEMDIR_RIGHTLEFT |
                         RA8875_MWCR0_WINC_ENABLE);
  ra8875_set_writecursor(priv, col, row);

  curhinc = -npixels;
  curvinc = 0;

#elif defined(CONFIG_LCD_PORTRAIT)

  row = (RA8875_YRES - 1) - row;

  /* Set the cursor position */

  ra8875_set_mwcr0(priv, RA8875_MWCR0_MODE_GRAPHICS |
                         RA8875_MWCR0_MEMDIR_TOPDOWN |
                         RA8875_MWCR0_WINC_ENABLE);
  ra8875_set_writecursor(priv, col, row);

  curhinc = 0;
  curvinc = npixels;

#else /* CONFIG_LCD_RPORTRAIT */

  col = (RA8875_XRES - 1) - col;

  /* Set the cursor position */

  ra8875_set_mwcr0(priv, RA8875_MWCR0_MODE_GRAPHICS |
                         RA8875_MWCR0_MEMDIR_DOWNTOP |
                         RA8875_MWCR0_WINC_ENABLE);
  ra8875_set_writecursor(priv, col, row);

  curhinc = 0;
  curvinc = -npixels;

#endif

  /* Write data, handle order automatically */

  lcd->pwrite_prepare(lcd, RA8875_MRWC);

  for (i = 0; i < npixels; i++)
    {
      /* Write the next pixel to this position */

#if RA8875_BPP == 8
      lcd->pwrite_data8(lcd, *src++);
#else
      lcd->pwrite_data16(lcd, *src++);
#endif
    }

  lcd->pwrite_finish(lcd);

  /* Update shadowed cursor */

  priv->shadow_w_curh += curhinc;
  priv->shadow_w_curv += curvinc;

  return OK;
}

/****************************************************************************
 * Name:  ra8875_getrun
 *
 * Description:
 *   This method can be used to read a partial raster line from the LCD:
 *
 *  dev     - The lcd device
 *  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 ra8875_getrun(FAR struct lcd_dev_s *dev,
                         fb_coord_t row, fb_coord_t col,
                         FAR uint8_t *buffer,
                         size_t npixels)
{
#ifndef CONFIG_LCD_NOGETRUN
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd = priv->lcd;
  FAR uint16_t *dest = (FAR uint16_t *)buffer;
  int i;

  /* Buffer must be provided and aligned to a 16-bit address boundary */

  ra8875_showrun(priv, row, col, npixels, false);
  DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);

#ifdef CONFIG_LCD_LANDSCAPE
  /* Set the cursor position */

  ra8875_putreg(lcd, RA8875_MWCR0, RA8875_MWCR0_MODE_GRAPHICS |
                                   RA8875_MWCR0_MEMDIR_LEFTRIGHT |
                                   RA8875_MWCR0_RINC_ENABLE);
  ra8875_putreg(lcd, RA8875_MRDC, RA8875_MRCD_MEMDIR_LEFTRIGHT);

  ra8875_set_readcursor(lcd, col, row);

#elif defined(CONFIG_LCD_RLANDSCAPE)
  /* Retransform coordinates, write right to left */

  col = (RA8875_XRES - 1) - col;
  row = (RA8875_YRES - 1) - row;

  /* Set the cursor position */

  ra8875_putreg(lcd, RA8875_MWCR0, RA8875_MWCR0_MODE_GRAPHICS |
                                   RA8875_MWCR0_MEMDIR_RIGHTLEFT |
                                   RA8875_MWCR0_RINC_ENABLE);
  ra8875_putreg(lcd, RA8875_MRDC, RA8875_MRCD_MEMDIR_RIGHTLEFT);

  ra8875_set_readcursor(lcd, col, row);

#elif defined(CONFIG_LCD_PORTRAIT)
  /* Retransform coordinates, write right to left */

  row = (RA8875_YRES - 1) - row;

  /* Set the cursor position */

  ra8875_putreg(lcd, RA8875_MWCR0, RA8875_MWCR0_MODE_GRAPHICS |
                                   RA8875_MWCR0_MEMDIR_TPDOWN |
                                   RA8875_MWCR0_RINC_ENABLE);
  ra8875_putreg(lcd, RA8875_MRDC, RA8875_MRCD_MEMDIR_TOPDOWN);

  ra8875_set_readcursor(lcd, col, row);

#else /* CONFIG_LCD_RPORTRAIT */
  /* Retransform coordinates, write right to left */

  col = (RA8875_XRES - 1) - col;

  /* Set the cursor position */

  ra8875_putreg(lcd, RA8875_MWCR0, RA8875_MWCR0_MODE_GRAPHICS |
                                   RA8875_MWCR0_MEMDIR_DOWNTOP |
                                   RA8875_MWCR0_RINC_ENABLE);
  ra8875_putreg(lcd, RA8875_MRDC, RA8875_MRCD_MEMDIR_DOWNTOP);

  ra8875_set_readcursor(lcd, col, row);

#endif

  /* Read data, handle order automatically */

  lcd->pread_prepare(lcd, RA8875_MRWC);

  for (i = 0; i < npixels; i++)
    {
      /* Read the next pixel from this position */

      *dest++ = lcd->pread_data16(lcd);
    }

  lcd->pread_finish(lcd);

  return OK;
#else
  return -ENOSYS;
#endif
}

/****************************************************************************
 * Name:  ra8875_getvideoinfo
 *
 * Description:
 *   Get information about the LCD video controller configuration.
 *
 ****************************************************************************/

static int ra8875_getvideoinfo(FAR struct lcd_dev_s *dev,
                               FAR struct fb_videoinfo_s *vinfo)
{
  DEBUGASSERT(dev && vinfo);
  lcdinfo("fmt: %d xres: %d yres: %d nplanes: 1\n",
          RA8875_COLORFMT, RA8875_XRES, RA8875_YRES);

  vinfo->fmt     = RA8875_COLORFMT;    /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
  vinfo->xres    = RA8875_XRES;        /* Horizontal resolution in pixel columns */
  vinfo->yres    = RA8875_YRES;        /* Vertical resolution in pixel rows */
  vinfo->nplanes = 1;                  /* Number of color planes supported */
  return OK;
}

/****************************************************************************
 * Name:  ra8875_getplaneinfo
 *
 * Description:
 *   Get information about the configuration of each LCD color plane.
 *
 ****************************************************************************/

static int ra8875_getplaneinfo(FAR struct lcd_dev_s *dev,
                               unsigned int planeno,
                               FAR struct lcd_planeinfo_s *pinfo)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;

  DEBUGASSERT(dev && pinfo && planeno == 0);
  lcdinfo("planeno: %d bpp: %d\n", planeno, RA8875_BPP);

  pinfo->putrun = ra8875_putrun;                  /* Put a run into LCD memory */
  pinfo->getrun = ra8875_getrun;                  /* Get a run from LCD memory */
  pinfo->buffer = (FAR uint8_t *)priv->runbuffer; /* Run scratch buffer */
  pinfo->bpp    = RA8875_BPP;                     /* Bits-per-pixel */
  pinfo->dev    = dev;                            /* The lcd device */
  return OK;
}

/****************************************************************************
 * Name:  ra8875_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 ra8875_getpower(FAR struct lcd_dev_s *dev)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;

  lcdinfo("power: %d\n", 0);
  return priv->power;
}

/****************************************************************************
 * Name:  ra8875_poweroff
 *
 * 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 ra8875_poweroff(FAR struct ra8875_lcd_s *lcd)
{
  /* Set the backlight off */

  ra8875_putreg(lcd, RA8875_P1CR, RA8875_P1CR_PWM_DISABLE);
  ra8875_putreg(lcd, RA8875_P1DCR, 0);

  /* Turn the display off */

  ra8875_putreg(lcd, RA8875_PWRR, RA8875_PWRR_DISPLAY_OFF);

  return OK;
}

/****************************************************************************
 * Name:  ra8875_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 ra8875_setpower(FAR struct lcd_dev_s *dev, int power)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  lcdinfo("power: %d\n", power);
  DEBUGASSERT((unsigned)power <= CONFIG_LCD_MAXPOWER);

  /* Set new power level */

  if (power > 0)
    {
      if (priv->power == 0)
        {
          /* Set the backlight level */

          ra8875_putreg(lcd, RA8875_P1CR, RA8875_P1CR_PWM_ENABLE);
          ra8875_putreg(lcd, RA8875_P1CR,
                        RA8875_P1CR_PWM_ENABLE | RA8875_P1CR_CSDIV(1));
        }

      ra8875_putreg(lcd, RA8875_P1DCR, power);

      /* Turn on display */

      ra8875_putreg(lcd, RA8875_PWRR, RA8875_PWRR_DISPLAY_ON);

      priv->power = power;
    }
  else
    {
      /* Turn the display off */

      ra8875_poweroff(lcd);

      /* Remember the power off state */

      priv->power = 0;
    }

  return OK;
}

/****************************************************************************
 * Name:  ra8875_getcontrast
 *
 * Description:
 *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
 *
 ****************************************************************************/

static int ra8875_getcontrast(FAR struct lcd_dev_s *dev)
{
  lcdinfo("Not implemented\n");
  return -ENOSYS;
}

/****************************************************************************
 * Name:  ra8875_setcontrast
 *
 * Description:
 *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
 *
 ****************************************************************************/

static int ra8875_setcontrast(FAR struct lcd_dev_s *dev,
                              unsigned int contrast)
{
  lcdinfo("contrast: %d\n", contrast);
  return -ENOSYS;
}

/****************************************************************************
 * Name:  ra8875_hwinitialize
 *
 * Description:
 *   Initialize the LCD hardware.
 *
 ****************************************************************************/

static inline int ra8875_hwinitialize(FAR struct ra8875_dev_s *priv)
{
  uint8_t rv;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  /* REVISIT: Maybe some of these values needs to be configurable?? */

  lcdinfo("hwinitialize\n");

  /* Reset */

  ra8875_putreg(lcd, RA8875_PWRR, RA8875_PWRR_SWRESET);
  up_mdelay(100);
  ra8875_putreg(lcd, RA8875_PWRR, 0);

  /* Setup the PLL config */

  ra8875_putreg(lcd, RA8875_PLLC1, RA8875_PLLC1_PLLDIVN(11));
  up_mdelay(10);
  ra8875_putreg(lcd, RA8875_PLLC2, RA8875_PLLC2_PLLDIVK(2));
  up_mdelay(10);

  /* Interface and color depth */

#if RA8875_BPP == 16
  rv = RA8875_SYSR_COLOR_65K;
#else
  rv = RA8875_SYSR_COLOR_256;
#endif
#if defined(CONFIG_LCD_RA8875_8BIT)
  rv |= RA8875_SYSR_MCUIF_8BIT;
#elif defined(CONFIG_LCD_RA8875_16BIT)
  rv |= RA8875_SYSR_MCUIF_16BIT;
#endif
  ra8875_putreg(lcd, RA8875_SYSR, rv);

  /* Pixel clock, invert + 4*SYS */

  ra8875_putreg(lcd, RA8875_PCSR,
                RA8875_PCSR_PCLK_INV | RA8875_PCSR_PERIOD_4SYS);
  up_mdelay(1);

  /* Horizontal Settings */

  ra8875_putreg(lcd, RA8875_HDWR, RA8875_HDWR_WIDTH(RA8875_HW_XRES));
  ra8875_putreg(lcd, RA8875_HNDFTR, 0x02);
  ra8875_putreg(lcd, RA8875_HNDR, 0x03);
  ra8875_putreg(lcd, RA8875_HSTR, 0x01);
  ra8875_putreg(lcd, RA8875_HPWR, 0x03);

  /* Vertical Settings */

  ra8875_putreg(lcd, RA8875_VDHR0, RA8875_VDHR0_HEIGHT(RA8875_HW_YRES));
  ra8875_putreg(lcd, RA8875_VDHR1, RA8875_VDHR1_HEIGHT(RA8875_HW_YRES));
  ra8875_putreg(lcd, RA8875_VNDR0, 0x0f);
  ra8875_putreg(lcd, RA8875_VNDR1, 0x00);
  ra8875_putreg(lcd, RA8875_VSTR0, 0x0e);
  ra8875_putreg(lcd, RA8875_VSTR1, 0x06);
  ra8875_putreg(lcd, RA8875_VPWR, 0x01);

#if !defined(RA8875_2LAYER_POSSIBLE)
  /* Too high, only one layer possible */

  ra8875_putreg(lcd, RA8875_DPCR, RA8875_DPCR_LAYERS_ONE);
#else
  /* Two layers */

  ra8875_putreg(lcd, RA8875_DPCR, RA8875_DPCR_LAYERS_TWO);
#endif

  /* Setup window to be full screen */

  ra8875_setwindow(lcd, 0, 0, RA8875_XRES, RA8875_YRES);

  /* Set background and foreground colors to black and white (respectively) */

  ra8875_setbackground(lcd, 0x0000);
  ra8875_setforeground(lcd, 0xffff);

  /* Clear the memory */

  ra8875_clearmem(lcd);

  ra8875_setpower(&priv->dev, 0x0);

  /* Initialize the shadow registers */

  priv->shadow_mwcr0 = 0;
  priv->shadow_w_curh = 0;
  priv->shadow_w_curv = 0;

  lcdinfo("hwinitialize done\n");
  return OK;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name:  ra8875_lcdinitialize
 *
 * Description:
 *   Initialize the LCD video hardware.
 *   The initial state of the LCD is fully initialized, display memory
 *   cleared,  and the LCD ready to use, but with the power setting at 0
 *   (full off).
 *
 ****************************************************************************/

FAR struct lcd_dev_s *ra8875_lcdinitialize(FAR struct ra8875_lcd_s *lcd)
{
  int ret;

  lcdinfo("Initializing\n");

  /* If we could support multiple RA8875 devices, this is where we would
   * allocate a new driver data structure.
   */

  FAR struct ra8875_dev_s *priv = &g_lcddev;

  /* Initialize the driver data structure */

  priv->dev.getvideoinfo = ra8875_getvideoinfo;
  priv->dev.getplaneinfo = ra8875_getplaneinfo;
  priv->dev.getpower     = ra8875_getpower;
  priv->dev.setpower     = ra8875_setpower;
  priv->dev.getcontrast  = ra8875_getcontrast;
  priv->dev.setcontrast  = ra8875_setcontrast;
  priv->lcd              = lcd;

  /* Configure and enable LCD */

  ret = ra8875_hwinitialize(priv);
  if (ret == OK)
    {
      /* Clear the display (setting it to the color 0=black) */

      ra8875_clear(&priv->dev, 0);

      /* Turn the display off */

      ra8875_poweroff(lcd);

      lcdinfo("Initialized\n");

      return &priv->dev;
    }

  return NULL;
}

/****************************************************************************
 * Name:  ra8875_clear
 *
 * Description:
 *   This is a non-standard LCD interface just for the RA8875.  Because
 *   of the various rotations, clearing the display in the normal way by
 *   writing a sequences of runs that covers the entire display can be very
 *   slow.  Here the display is cleared by simply setting all video memory to
 *   the specified color.
 *
 *   NOTE: This function is not available to applications in the protected
 *   or kernel build modes.
 *
 ****************************************************************************/

void ra8875_clear(FAR struct lcd_dev_s *dev, uint16_t color)
{
#if 0
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  /* Set the color to use for filling */

  ra8875_setforeground(lcd, color);

  /* Draw a rectangle over entire screen */

  ra8875_putreg16(lcd, RA8875_DLHSR0, 0);
  ra8875_putreg16(lcd, RA8875_DLVSR0, 0);
  ra8875_putreg16(lcd, RA8875_DLHER0, RA8875_XRES);
  ra8875_putreg16(lcd, RA8875_DLVER0, RA8875_YRES);

  ra8875_putreg(lcd, RA8875_DCR, RA8875_DCR_FILL | RA8875_DCR_SQUARE);
  ra8875_putreg(lcd, RA8875_DCR, RA8875_DCR_FILL | RA8875_DCR_SQUARE |
                                 RA8875_DCR_LINE_START);

  ra8875_waitreg(lcd, RA8875_DCR, RA8875_DCR_LINE_START);
#endif

  /* Draw a rectangle over entire screen */

  ra8875_drawrectangle(dev, 0, 0, RA8875_XRES, RA8875_YRES, color, true);
}

/****************************************************************************
 * Name:  ra8875_drawrectangle
 *
 *   This is a non-standard function to draw a rectangle on the LCD.
 *   This function is also used internally as part of the ra8875_clear
 *   implementation
 *
 *   NOTE: This non-standard function is not available to applications in
 *   the protected or kernel build modes.
 *
 ****************************************************************************/

void ra8875_drawrectangle(FAR struct lcd_dev_s *dev,
                          uint16_t x, uint16_t y,
                          uint16_t width, uint16_t height,
                          uint16_t color, bool fill)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  uint8_t draw_cmd = RA8875_DCR_SQUARE;
  uint16_t sx;
  uint16_t sy;
  uint16_t ex;
  uint16_t ey;

  /* Set the color to use for filling */

  ra8875_setforeground(lcd, color);

  /* Handle degenerate cases */

  if (width == 1)
    {
      ra8875_drawline(dev, x, y, x, y + height, color);
    }
  else if (height == 1)
    {
      ra8875_drawline(dev, x, y, x + width, y, color);
    }

  if (fill)
    {
      draw_cmd |= RA8875_DCR_FILL;
    }

  /* Setup coordinates */

#ifdef CONFIG_LCD_LANDSCAPE

  sx = x;
  sy = y;
  ex = x + width - 1;
  ey = y + height - 1;

#elif defined(CONFIG_LCD_RLANDSCAPE)

  sx = (RA8875_XRES - 1) - x - width + 1;
  sy = (RA8875_YRES - 1) - y - height + 1;
  ex = (RA8875_XRES - 1) - x;
  ey = (RA8875_YRES - 1) - y;

#elif defined(CONFIG_LCD_PORTRAIT)

  sx = (RA8875_YRES - 1) - y - height + 1;
  sy = x;
  ex = (RA8875_YRES - 1) - y;
  ey = x + width - 1;

#else /* CONFIG_LCD_RPORTRAIT */

  sx = y;
  sy = (RA8875_XRES - 1) - x - width + 1;
  ex = y + height - 1;
  ey = (RA8875_XRES - 1) - x;

#endif

  ra8875_putreg16(lcd, RA8875_DLHSR0, sx);
  ra8875_putreg16(lcd, RA8875_DLVSR0, sy);
  ra8875_putreg16(lcd, RA8875_DLHER0, ex);
  ra8875_putreg16(lcd, RA8875_DLVER0, ey);

  /* Run drawing */

  ra8875_putreg(lcd, RA8875_DCR, draw_cmd);
  ra8875_putreg(lcd, RA8875_DCR, draw_cmd | RA8875_DCR_LINE_START);

  ra8875_waitreg(lcd, RA8875_DCR, RA8875_DCR_LINE_START);
}

/****************************************************************************
 * Name:  ra8875_drawline
 *
 * Description:
 *   This is a non-standard function to draw a line on the LCD.
 *   This function is also used internally as part of the
 *   ra8875_rectandle implementation.
 *
 *   NOTE: This non-standard function is not available to applications in
 *   the protected or kernel build modes.
 *
 ****************************************************************************/

void ra8875_drawline(FAR struct lcd_dev_s *dev,
                     uint16_t x1, uint16_t y1,
                     uint16_t x2, uint16_t y2,
                     uint16_t color)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  uint8_t draw_cmd = RA8875_DCR_LINE;
  uint16_t sx;
  uint16_t sy;
  uint16_t ex;
  uint16_t ey;

  /* Set the color to use for filling */

  ra8875_setforeground(lcd, color);

  /* Handle degenerate cases */

  /* Setup coordinates */

#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)

  sx = x1;
  sy = y1;
  ex = x2;
  ey = y2;

#else /* CONFIG_LCD_PORTRAIT or CONFIG_LCD_RPORTRAIT */

  sx = y1;
  sy = x1;
  ex = y2;
  ey = x2;

#endif

  ra8875_putreg16(lcd, RA8875_DLHSR0, sx);
  ra8875_putreg16(lcd, RA8875_DLVSR0, sy);
  ra8875_putreg16(lcd, RA8875_DLHER0, ex);
  ra8875_putreg16(lcd, RA8875_DLVER0, ey);

  /* Run drawing */

  ra8875_putreg(lcd, RA8875_DCR, draw_cmd);
  ra8875_putreg(lcd, RA8875_DCR, draw_cmd | RA8875_DCR_LINE_START);

  ra8875_waitreg(lcd, RA8875_DCR, RA8875_DCR_LINE_START);
}

/****************************************************************************
 * Name:  ra8875_drawtriangle
 *
 * Description:
 *   This is a non-standard function to draw a triangle on the LCD.
 *   This function is also used internally as part of the ra8875_rectandle
 *   implementation.
 *
 *   NOTE: This non-standard function is not available to applications in the
 *   protected or kernel build modes.
 *
 ****************************************************************************/

#ifdef CONFIG_LCD_RA8875_EXTENDED
void ra8875_drawtriangle(FAR struct lcd_dev_s *dev,
                         uint16_t x0, uint16_t y0, uint16_t x1,
                         uint16_t y1, uint16_t x2, uint16_t y2,
                         uint16_t color, bool fill)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  uint8_t draw_cmd = RA8875_DCR_TRIANGLE;
  uint16_t _x0;
  uint16_t _x1;
  uint16_t _x2;
  uint16_t _y0;
  uint16_t _y1;
  uint16_t _y2;

  /* Set the color to use for filling */

  ra8875_setforeground(lcd, color);

  if (fill)
    {
      draw_cmd |= RA8875_DCR_FILL;
    }

  /* Setup coordinates */

#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)

  _x0 = x0;
  _x1 = x1;
  _x2 = x2;

  _y0 = y0;
  _y1 = y1;
  _y2 = y2;

#else /* CONFIG_LCD_PORTRAIT or CONFIG_LCD_RPORTRAIT */

  _x0 = y0;
  _x1 = y1;
  _x2 = y2;

  _y0 = x0;
  _y1 = x1;
  _y2 = x2;

#endif

  ra8875_putreg16(lcd, RA8875_DLHSR0, _x0);
  ra8875_putreg16(lcd, RA8875_DLVSR0, _y0);
  ra8875_putreg16(lcd, RA8875_DLHER0, _x1);
  ra8875_putreg16(lcd, RA8875_DLVER0, _y1);
  ra8875_putreg16(lcd, RA8875_DTPH0, _x2);
  ra8875_putreg16(lcd, RA8875_DTPV0, _y2);

  /* Run drawing */

  ra8875_putreg(lcd, RA8875_DCR, draw_cmd);
  ra8875_putreg(lcd, RA8875_DCR, draw_cmd | RA8875_DCR_LINE_START);

  ra8875_waitreg(lcd, RA8875_DCR, RA8875_DCR_LINE_START);
}
#endif

/****************************************************************************
 * Name:  ra8875_drawcircle
 *
 * Description:
 *   This is a non-standard function to draw a circle on the LCD.
 *   This function is also used internally as part of the ra8875_rectandle
 *   implementation.
 *
 *   NOTE: This non-standard function is not available to applications in the
 *   protected or kernel build modes.
 *
 ****************************************************************************/

#ifdef CONFIG_LCD_RA8875_EXTENDED
void ra8875_drawcircle(FAR struct lcd_dev_s *dev,
                       uint16_t x, uint16_t y, uint8_t radius,
                       uint16_t color, bool fill)
{
  FAR struct ra8875_dev_s *priv = (FAR struct ra8875_dev_s *)dev;
  FAR struct ra8875_lcd_s *lcd  = priv->lcd;

  uint8_t draw_cmd = 0;
  uint16_t _x;
  uint16_t _y;

  /* Set the color to use for filling */

  ra8875_setforeground(lcd, color);

  if (fill)
    {
      draw_cmd |= RA8875_DCR_FILL;
    }

  /* Setup coordinates */

#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)

  _x = x;
  _y = y;

#else /* CONFIG_LCD_PORTRAIT or CONFIG_LCD_RPORTRAIT */

  _x = y;
  _y = x;

#endif

  ra8875_putreg16(lcd, RA8875_DCHR0, _x);
  ra8875_putreg16(lcd, RA8875_DCVR0, _y);
  ra8875_putreg(lcd, RA8875_DCRR, radius);

  /* Run drawing */

  ra8875_putreg(lcd, RA8875_DCR, draw_cmd);
  ra8875_putreg(lcd, RA8875_DCR, draw_cmd | RA8875_DCR_CIRCLE_START);

  ra8875_waitreg(lcd, RA8875_DCR, RA8875_DCR_CIRCLE_START);
}
#endif

#endif /* CONFIG_LCD_RA8875 */