/************************************************************************************
 * configs/sam4e-ek/src/sam_ili9335.c
 *
 *   Copyright (C) 2014 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * References:
 * - This driver is a modification of the Shenzhou ILI9325 LCD driver.
 * - ILI9325 Datasheet, Version: V0.43, ILI9325DS_V0.43.pdf, ILI TECHNOLOGY CORP.,
 * - SAM4Ex Datasheet, Atmel
 * - Atmel ILI9325 Sample code for the SAM4S
 *
 * Some the LCD and SMC initialization logic comes from Atmel sample code for the
 * SAM4S.  The Atmel sample code has a BSD-like license with an additional
 * requirement that restricts the code from being used on anything but Atmel
 * microprocessors.  I do not believe that this file "derives" from the Atmel
 * sample code nor do I believe that it contains anything but generally available
 * ILI9325 and SAM4x logic.  Credit, however, needs to go where it is due.
 *
 * 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.
 *
 ************************************************************************************/

/**************************************************************************************
 *
 * The SAM4E-EK carries a TFT transmissive LCD module with touch panel, FTM280C34D.
 * Its integrated driver IC is ILI9325. The LCD display area is 2.8 inches diagonally
 * measured, with a native resolution of 240 x 320 dots.
 *
 * The SAM4E16 communicates with the LCD through PIOC where an 8-bit parallel "8080-
 * like" protocol data bus has to be implemented in software.
 *
 *  ---- ----- --------- --------------------------------
 *  PIN  PIO   SIGNAL    NOTES
 *  ---- ----- --------- --------------------------------
 *    1                  VDD
 *    2  PC7   DB17
 *    3  PC6   DB16
 *    4  PC5   DB15
 *    5  PC4   DB14
 *    6  PC3   DB13
 *    7  PC2   DB12
 *    8  PC1   DB11
 *    9  PC0   DB10
 *   10        DB9       Pulled low
 *   11        DB8       Pulled low
 *   12        DB7       Pulled low
 *   13        DB6       Pulled low
 *   14        DB5       Pulled low
 *   15        DB4       Pulled low
 *   16        DB3       Pulled low
 *   17        DB2       Pulled low
 *   18        DB1       Pulled low
 *   19        DB0       Pulled low
 *  ---- ----- --------- --------------------------------
 *   20                  VDD
 *   21  PC11  RD
 *   22  PC8   WR
 *   23  PC19  RS
 *   24  PD18  CS        Via J8, pulled high.
 *   25        RESET     Connects to NSRST
 *   26        IM0       Pulled high
 *   27        IM1       Grounded
 *   28        GND
 *  ---- ----- --------- --------------------------------
 *   29 [PC13] LED-A     Backlight controls:  PC13 enables
 *   30 [PC13] LEDK1       AAT3155 charge pump that drives
 *   31 [PC13] LEDK2       the backlight LEDs
 *   32 [PC13] LEDK3
 *   33 [PC13] LEDK4
 *   34 [PC13] LEDK1
 *  ---- ----- --------- --------------------------------
 *   35        Y+        These go to the ADS7843
 *   36        Y-          touchscreen controller.
 *   37        X+
 *   38        X-
 *   39        NC
 *  ---- ----- --------- --------------------------------
 *
 * LCD backlight is made of 4 white chip LEDs in parallel, driven by an AAT3155
 * charge pump, MN4. The AAT3155 is controlled by the SAM3U4E through a single line
 * Simple Serial Control (S2Cwire) interface, which permits to enable, disable, and
 * set the LED drive current (LED brightness control) from a 32-level logarithmic
 * scale. Four resistors R93/R94/R95/R96 are implemented for optional current
 * limitation.
 *
 **************************************************************************************/

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

#include <nuttx/config.h>

#include <sys/types.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/arch.h>
#include <nuttx/board.h>
#include <nuttx/lcd/lcd.h>
#include <nuttx/lcd/ili9325.h>
#include <nuttx/video/rgbcolors.h>

#include <arch/irq.h>
#include <arch/board/board.h>

#include "up_arch.h"
#include "sam_gpio.h"
#include "sam_periphclks.h"
#include "chip/sam_pmc.h"
#include "chip/sam_smc.h"
#include "sam4e-ek.h"

#ifdef CONFIG_LCD

/************************************************************************************
 * Pre-processor Definitions
 ************************************************************************************/
/* SMC must be selected */

#if !defined(CONFIG_SAM34_SMC)
#  error "CONFIG_SAM34_SMC is required"
#endif

/* Check contrast selection */

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

/* Check power setting */

#if !defined(CONFIG_LCD_MAXPOWER)
#  define CONFIG_LCD_MAXPOWER 16
#endif

#if CONFIG_LCD_MAXPOWER < 16
#  error CONFIG_LCD_MAXPOWER should be >= 16
#  undef CONFIG_LCD_MAXPOWER
#  define CONFIG_LCD_MAXPOWER 16
#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

/* Background color */

#if !defined(CONFIG_SAM4EEK_LCD_BGCOLOR)
#  define CONFIG_SAM4EEK_LCD_BGCOLOR 0
#endif

/* Define CONFIG_DEBUG_LCD to enable detailed LCD debug output. Verbose debug must
 * also be enabled.
 */

#ifndef CONFIG_DEBUG
#  undef CONFIG_DEBUG_VERBOSE
#  undef CONFIG_DEBUG_GRAPHICS
#  undef CONFIG_DEBUG_LCD
#  undef CONFIG_LCD_REGDEBUG
#endif

#ifndef CONFIG_DEBUG_VERBOSE
#  undef CONFIG_DEBUG_LCD
#endif

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

#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RLANDSCAPE)
#  define SAM_XRES            320
#  define SAM_YRES            240
#else
#  define SAM_XRES            240
#  define SAM_YRES            320
#endif

/* Color depth and format */

#if defined(CONFIG_SAM4EEK_LCD_RGB565)
#  define SAM_BPP             16
#  define SAM_COLORFMT        FB_FMT_RGB16_565
#elif defined(CONFIG_SAM4EEK_LCD_RGB24)
#  define SAM_BPP             24
#  define SAM_COLORFMT        FB_FMT_RGB24
#else /* if defined(CONFIG_SAM4EEK_LCD_RGB32) -- without ALPHA */
#  define SAM_BPP             32
#  define SAM_COLORFMT        FB_FMT_RGB32
#endif

/* Color decoding macros */

#ifdef CONFIG_SAM4EEK_LCD_RGB565
#  define RGB_RED(rgb)        (((rgb) >> 8)  & 0xf8)
#  define RGB_GREEN(rgb)      (((rgb) >> 3)  & 0xfc)
#  define RGB_BLUE(rgb)       (((rgb) << 3)  & 0xf8)
#else /* RGB888 or RGB32 without ALPHA */
#  define RGB_RED(rgb)        (((rgb) >> 16) & 0xff)
#  define RGB_GREEN(rgb)      (((rgb) >> 8)  & 0xff)
#  define RGB_BLUE(rgb)       ( (rgb)        & 0xff)
#endif

/* SAM4E-EK LCD Hardware Definitions **************************************************/
/* LCD /CS is CE4,  Bank 3 of NOR/SRAM Bank 1~4 */

#define SAM_LCD_BASE          ((uintptr_t)SAM_EXTCS1_BASE)
#define LCD_INDEX             (*(volatile uint8_t *)(SAM_LCD_BASE))
#define LCD_DATA              (*(volatile uint8_t *)(SAM_LCD_BASE + 2))

/* LCD SMC chip select number to be set */

#define SAM_LCD_CS            1

/* Backlight */

#define BKL_LEVELS            16
#define BKL_PULSE_DURATION    24
#define BKL_ENABLE_DURATION   (128*1024)
#define BKL_DISABLE_DURATION  (128*1024)

/* Debug ******************************************************************************/

#ifdef CONFIG_DEBUG_LCD
#  define lcddbg              dbg
#  define lcdvdbg             vdbg
#else
#  define lcddbg(x...)
#  define lcdvdbg(x...)
#endif

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

/* Type definition for the correct size of one pixel (from the application standpoint). */

#ifdef CONFIG_SAM4EEK_LCD_RGB565
typedef uint16_t sam_color_t;
#else /* RGB888 or RGB32 (without ALPHA) */
typedef uint32_t sam_color_t;
#endif

/* This structure describes the LCD registers */

struct lcd_regs_s
{
  volatile uint16_t index;
  volatile uint16_t value;
};

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

struct sam_dev_s
{
  /* Publicly visible device structure */

  struct lcd_dev_s dev;

  /* Private LCD-specific information follows */

  uint8_t  power;       /* Current power setting */
  bool     output;      /* True: Configured for output */
};

/************************************************************************************
 * Private Function Prototypes
 ************************************************************************************/
/* Low Level LCD access */

static void sam_write_reg(uint8_t regaddr, uint16_t regval);
static uint16_t sam_read_reg(uint8_t regaddr);
static inline void sam_gram_prepare(void);
static inline void sam_gram_write(sam_color_t color);
static inline sam_color_t sam_gram_read(void);
static void sam_set_cursor(uint16_t col, uint16_t row);

/* Backlight/power controls */

static void sam_disable_backlight(void);
static void sam_set_backlight(unsigned int power);
static int sam_poweroff(FAR struct sam_dev_s *priv);

/* LCD Data Transfer Methods */

static int sam_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
             size_t npixels);
static int sam_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
             size_t npixels);

/* LCD Configuration */

static int sam_getvideoinfo(FAR struct lcd_dev_s *dev,
             FAR struct fb_videoinfo_s *vinfo);
static int sam_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 sam_getpower(struct lcd_dev_s *dev);
static int sam_setpower(struct lcd_dev_s *dev, int power);
static int sam_getcontrast(struct lcd_dev_s *dev);
static int sam_setcontrast(struct lcd_dev_s *dev, unsigned int contrast);

/* Initialization */

static void sam_gpio_initialize(void);
static inline void sam_smc_initialize(void);
static void sam_lcd9325_initialize(void);
static inline int sam_lcd_initialize(void);

/************************************************************************************
 * Private Data
 ************************************************************************************/
/* LCD GPIO configurations */

static const uint32_t g_lcdpin[] =
{
  GPIO_SMC_D0,  GPIO_SMC_D1,  GPIO_SMC_D2,  GPIO_SMC_D3,      /* D0-D3 */
  GPIO_SMC_D4,  GPIO_SMC_D5,  GPIO_SMC_D6,  GPIO_SMC_D7,      /* D4-D7 */
  GPIO_SMC_NRD, GPIO_SMC_NWE, GPIO_SMC_A1,  GPIO_SMC_NCS1_2,  /* RD, WR, RS, CS */
  GPIO_LCD_BKL                                                /* Backlight control */
};

#define LCD_NPINS (sizeof(g_lcdpin) / sizeof(uint32_t))

/* 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 uint16_t g_runbuffer[SAM_XRES];

/* This structure describes the overall LCD video controller */

static const struct fb_videoinfo_s g_videoinfo =
{
  .fmt     = SAM_COLORFMT,         /* Color format: RGB16-565: RRRR RGGG GGGB BBBB */
  .xres    = SAM_XRES,             /* Horizontal resolution in pixel columns */
  .yres    = SAM_YRES,             /* Vertical resolution in pixel rows */
  .nplanes = 1,                    /* Number of color planes supported */
};

/* This is the standard, NuttX Plane information object */

static const struct lcd_planeinfo_s g_planeinfo =
{
  .putrun = sam_putrun,            /* Put a run into LCD memory */
  .getrun = sam_getrun,            /* Get a run from LCD memory */
  .buffer = (uint8_t*)g_runbuffer, /* Run scratch buffer */
  .bpp    = SAM_BPP,               /* Bits-per-pixel */
};

/* This is the standard, NuttX LCD driver object */

static struct sam_dev_s g_lcddev =
{
  .dev =
  {
    /* LCD Configuration */

    .getvideoinfo = sam_getvideoinfo,
    .getplaneinfo = sam_getplaneinfo,

    /* LCD RGB Mapping -- Not supported */
    /* Cursor Controls -- Not supported */

    /* LCD Specific Controls */

    .getpower     = sam_getpower,
    .setpower     = sam_setpower,
    .getcontrast  = sam_getcontrast,
    .setcontrast  = sam_setcontrast,
  },
};

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

/************************************************************************************
 * Name:  sam_write_reg
 *
 * Description:
 *   Write to an LCD register
 *
 ************************************************************************************/

static void sam_write_reg(uint8_t regaddr, uint16_t regval)
{
  LCD_INDEX = 0;
  LCD_INDEX = regaddr;

  /* Write the 16-bit register value */

  LCD_DATA  = (uint8_t)(regval >> 8);
  LCD_DATA  = (uint8_t)(regval & 0xff);
}

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

static uint16_t sam_read_reg(uint8_t regaddr)
{
  uint16_t regval;

  LCD_INDEX = 0;
  LCD_INDEX = regaddr;

  /* Read and return the 16-bit register contents */

  regval = (uint16_t)LCD_DATA;
  regval = (regval << 8) | (uint16_t)LCD_DATA;

  return regval;
}

/************************************************************************************
 * Name:  sam_gram_prepare
 *
 * Description:
 *   Setup to read or write multiple pixels to the GRAM memory
 *
 ************************************************************************************/

static inline void sam_gram_prepare(void)
{
  LCD_INDEX = 0;
  LCD_INDEX = ILI9325_GRAM_DATA_REG;
}

/************************************************************************************
 * Name:  sam_gram_write
 *
 * Description:
 *   Write one pixel to the GRAM memory
 *
 ************************************************************************************/

static inline void sam_gram_write(sam_color_t color)
{
  LCD_DATA = RGB_RED(color);
  LCD_DATA = RGB_GREEN(color);
  LCD_DATA = RGB_BLUE(color);
}

/************************************************************************************
 * Name:  sam_gram_read
 *
 * Description:
 *   Read one 16-bit pixel to the GRAM memory
 *
 ************************************************************************************/

static inline sam_color_t sam_gram_read(void)
{
  uint8_t value[2];

  value[0] = LCD_DATA;       /* Dummy read */
  value[1] = LCD_DATA;       /* Dummy read */
  value[0] = LCD_DATA;       /* RGB565 data upper byte */
  value[1] = LCD_DATA;       /* RGB565 data lower byte */

  /* Convert and transfer the color to the user buffer */

#if defined(CONFIG_SAM4EEK_LCD_RGB565)
  /* Return the raw RGB565 color */

  return (sam_color_t)value[0] << 8 | (sam_color_t)value[1];

#else /* if defined(CONFIG_SAM4EEK_LCD_RGB24) || defined(CONFIG_SAM4EEK_LCD_RGB32) */
      /* RRRR RGGG GGGB BBBB -> 0000 0000 RRRR R000 GGGG GG00 BBBB B000 */

  return ((value[0] & 0xf8)) |
         ((value[0] & 0x07) << 13) | ((value[1] & 0xE0) << 5) |
         ((value[1] & 0x1f) << 19);
#endif
}

/************************************************************************************
 * Name:  sam_set_cursor
 *
 * Description:
 *   Set the cursor position.  In landscape mode, the "column" is actually the physical
 *   Y position and the "row" is the physical X position.
 *
 ************************************************************************************/

static void sam_set_cursor(uint16_t col, uint16_t row)
{
  sam_write_reg(ILI9325_HORIZONTAL_GRAM_ADDR_SET, row);
  sam_write_reg(ILI9325_VERTICAL_GRAM_ADDR_SET, col);
}

/************************************************************************************
 * Name:  sam_dumprun
 *
 * Description:
 *   Dump the contexts of the run buffer:
 *
 *  run     - The buffer in containing the run read to be dumped
 *  npixels - The number of pixels to dump
 *
 ************************************************************************************/

#if 0 /* Sometimes useful */
static void sam_dumprun(FAR const char *msg, FAR uint16_t *run, size_t npixels)
{
  int i, j;

  syslog(LOG_DEBUG, "\n%s:\n", msg);
  for (i = 0; i < npixels; i += 16)
    {
      up_putc(' ');
      syslog(LOG_DEBUG, " ");
      for (j = 0; j < 16; j++)
        {
          syslog(LOG_DEBUG, " %04x", *run++);
        }
      up_putc('\n');
    }
}
#endif

/************************************************************************************
 * Name:  sam_disable_backlight
 *
 * Description:
 *   Turn the backlight off.
 *
 ************************************************************************************/

static void sam_disable_backlight(void)
{
  volatile int delay;

  sam_gpiowrite(GPIO_LCD_BKL, false);
  for (delay = 0; delay < BKL_DISABLE_DURATION; delay++);
}

/************************************************************************************
 * Name:  sam_set_backlight
 *
 * Description:
 *   The the backlight to the level associated with the specified power value.
 *
 ************************************************************************************/

static void sam_set_backlight(unsigned int power)
{
  volatile int delay;
  unsigned int level;
  int i;

  /* Scale the power setting to the range 1...BKL_LEVELS */

  DEBUGASSERT(power > 0 && power <= CONFIG_LCD_MAXPOWER);
  level = (power * BKL_LEVELS) / CONFIG_LCD_MAXPOWER;
  if (level < 1)
    {
      level = 1;
    }

  level = BKL_LEVELS - level + 1;

  /* Set the new backlight level */

  for (i = 0; i < level; i++)
    {
      /* Generate a pulse to the charge pump */

      sam_gpiowrite(GPIO_LCD_BKL, false);
      for (delay = 0; delay < BKL_PULSE_DURATION; delay++);

      sam_gpiowrite(GPIO_LCD_BKL, true);
      for (delay = 0; delay < BKL_PULSE_DURATION; delay++);
    }

  /* Lock in this level */

  for (delay = 0; delay < BKL_ENABLE_DURATION; delay++);
}

/************************************************************************************
 * Name:  sam_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 sam_poweroff(FAR struct sam_dev_s *priv)
{
  /* Turn the display off */

  sam_write_reg(ILI9325_DISP_CTRL1, 0);

  /* Disable the backlight */

  sam_disable_backlight();

  /* Remember the power off state */

  priv->power = 0;
  return OK;
}

/************************************************************************************
 * Name:  sam_putrun
 *
 * Description:
 *   This method can be used to write a partial raster line to the LCD:
 *
 *   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 sam_putrun(fb_coord_t row, fb_coord_t col, FAR const uint8_t *buffer,
                       size_t npixels)
{
#if defined(CONFIG_SAM4EEK_LCD_RGB565)
  FAR const uint16_t *src = (FAR const uint16_t*)buffer;
#elif defined(CONFIG_SAM4EEK_LCD_RGB24)
  FAR const uint8_t  *src = (FAR const uint8_t*)buffer;
#elif defined(CONFIG_SAM4EEK_LCD_RGB32)
  FAR const uint32_t *src = (FAR const uint32_t*)buffer;
#endif

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

  lcdvdbg("row: %d col: %d npixels: %d\n", row, col, npixels);
  DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);

  /* Set the cursor position */

  sam_set_cursor(col, row);

  /* Prepare to write GRAM data */

  sam_gram_prepare();

  /* Then transfer the pixels as 3 8-bit transfers, each providing 6 bits of the
   * color component in the MS bits.
   */

#if defined(CONFIG_SAM4EEK_LCD_RGB565) || defined(CONFIG_SAM4EEK_LCD_RGB32)
  while (npixels--)
    {
      sam_color_t color = *src++;
      LCD_DATA = RGB_RED(color);
      LCD_DATA = RGB_GREEN(color);
      LCD_DATA = RGB_BLUE(color);
    }
#else /* defined(CONFIG_SAM4EEK_LCD_RGB24) */
  while (npixels--)
    {
      LCD_DATA      = *src++;
      LCD_DATA      = *src++;
      LCD_DATA      = *src++;
    }
#endif

  return OK;
}

/************************************************************************************
 * Name:  sam_getrun
 *
 * Description:
 *   This method can be used to read a partial raster line from the LCD:
 *
 *  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 sam_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer,
                      size_t npixels)
{
  uint8_t value[2];
#if defined(CONFIG_SAM4EEK_LCD_RGB24)
  FAR uint8_t *ptr = (FAR uint8_t *)buffer;
#endif
  /* Set the cursor position */

  sam_set_cursor(col, row);

  /* Prepare to write GRAM data */

  sam_gram_prepare();

  /* Then transfer the pixels, reading RGB565 and converting this to the format
   * expected by the caller.
   */

  while (npixels--)
    {
      /* Read the RGB565 colot */

      value[0] = LCD_DATA;       /* Dummy read */
      value[1] = LCD_DATA;       /* Dummy read */
      value[0] = LCD_DATA;       /* Data upper byte */
      value[1] = LCD_DATA;       /* Data lower byte */

      /* Convert and transfer the color to the user buffer */

#if defined(CONFIG_SAM4EEK_LCD_RGB565)
      /* Return the raw RGB565 color */

      *buffer++ = (sam_color_t)value[0] << 8 | (sam_color_t)value[1];

#elif defined(CONFIG_SAM4EEK_LCD_RGB24)
      /* RRRR RGGG GGGB BBBB -> RRRR R000, GGGG GG00, BBBB B000 */

      *ptr++ = (value[0] & 0xf8);
      *ptr++ = (value[0] & 0x07) << 5) | ((value[1] & 0xe0) >> 3);
      *ptr++ = (value[1] & 0x1f) << 3;

#else /* if defined(CONFIG_SAM4EEK_LCD_RGB32) */
      /* RRRR RGGG GGGB BBBB -> 0000 0000 RRRR R000 GGGG GG00 BBBB B000 */

      *buffer++ = ((value[0] & 0xf8)) |
                  ((value[0] & 0x07) << 13) | ((value[1] & 0xE0) << 5) |
                  ((value[1] & 0x1f) << 19);
#endif
    }

  return OK;
}

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

static int sam_getvideoinfo(FAR struct lcd_dev_s *dev,
                            FAR struct fb_videoinfo_s *vinfo)
{
  DEBUGASSERT(dev && vinfo);
  lcdvdbg("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:  sam_getplaneinfo
 *
 * Description:
 *   Get information about the configuration of each LCD color plane.
 *
 ************************************************************************************/

static int sam_getplaneinfo(FAR struct lcd_dev_s *dev, unsigned int planeno,
                              FAR struct lcd_planeinfo_s *pinfo)
{
  DEBUGASSERT(dev && pinfo && planeno == 0);
  lcdvdbg("planeno: %d bpp: %d\n", planeno, g_planeinfo.bpp);
  memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
  return OK;
}

/************************************************************************************
 * Name:  sam_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 sam_getpower(struct lcd_dev_s *dev)
{
  FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev;

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

/************************************************************************************
 * Name:  sam_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 sam_setpower(struct lcd_dev_s *dev, int power)
{
  FAR struct sam_dev_s *priv = (FAR struct sam_dev_s *)dev;

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

  /* Set new power level */

  if (power > 0)
    {
      /* Then turn the display on */

      sam_write_reg(ILI9325_DISP_CTRL1,
                    ILI9325_DISP_CTRL1_BASEE | ILI9325_DISP_CTRL1_GON |
                    ILI9325_DISP_CTRL1_DTE | ILI9325_DISP_CTRL1_D(3));

      /* Set the backlight level */

      sam_set_backlight((unsigned int)power);
      up_mdelay(50);
      priv->power = power;
    }
  else
    {
      /* Turn the display off */

      sam_poweroff(priv);
    }

  return OK;
}

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

static int sam_getcontrast(struct lcd_dev_s *dev)
{
  lcdvdbg("Not implemented\n");
  return -ENOSYS;
}

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

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

/************************************************************************************
 * Name:  sam_gpio_initialize
 *
 * Description:
 *   Configure LCD GPIO pins
 *
 ************************************************************************************/

static inline void sam_gpio_initialize(void)
{
  int i;

  /* Configure all LCD pins pins (backlight is initially off) */

  for (i = 0; i < LCD_NPINS; i++)
    {
      sam_configgpio(g_lcdpin[i]);
    }
}

/************************************************************************************
 * Name:  sam_smc_initialize
 *
 * Description:
 *   Configure LCD SMC interface
 *
 ************************************************************************************/

static inline void sam_smc_initialize(void)
{
  uintptr_t smcbase =  SAM_SMCCS_BASE(SAM_LCD_CS);
  uint32_t regval;

  /* Configure SMC interface for the LCD */

  regval = SMCCS_SETUP_NWESETUP(2) | SMCCS_SETUP_NCSWRSETUP(2) |
           SMCCS_SETUP_NRDSETUP(2) | SMCCS_SETUP_NCSRDSETUP(2);
  putreg32(regval, smcbase + SAM_SMCCS_SETUP_OFFSET);

  regval = SMCCS_PULSE_NWEPULSE(4)  | SMCCS_PULSE_NCSWRPULSE(4) |
           SMCCS_PULSE_NRDPULSE(10) | SMCCS_PULSE_NCSRDPULSE(10);
  putreg32(regval, smcbase + SAM_SMCCS_PULSE_OFFSET);

  regval = SMCCS_CYCLE_NWECYCLE(10) | SMCCS_CYCLE_NRDCYCLE(22);
  putreg32(regval, smcbase + SAM_SMCCS_CYCLE_OFFSET);

#ifdef SMCCS_MODE_DBW_8BITS /* SAM3U, SAM3X, SAM3A */
  regval = SMCCS_MODE_READMODE | SMCCS_MODE_WRITEMODE | SMCCS_MODE_DBW_8BITS;
#else
  regval = SMCCS_MODE_READMODE | SMCCS_MODE_WRITEMODE;
#endif
  putreg32(regval, smcbase + SAM_SMCCS_MODE_OFFSET);
}

/************************************************************************************
 * Name:  sam_lcd9325_initialize
 *
 * Description:
 *   Initialize the ILI9325 LCD.
 *
 ************************************************************************************/

static void sam_lcd9325_initialize(void)
{
  uint16_t regval;

  /* Turn off the LCD ***************************************************************/

  sam_write_reg(ILI9325_DISP_CTRL1,
                ILI9325_DISP_CTRL1_GON | ILI9325_DISP_CTRL1_DTE |
                ILI9325_DISP_CTRL1_D(3));

  /* Initial sequence ***************************************************************/
  /* Disable sleep and standby mode*/

  sam_write_reg(ILI9325_POWER_CTRL1, 0);

  /* Start internal OSC */

  sam_write_reg(ILI9325_START_OSC_CTRL, ILI9325_START_OSC_CTRL_EN);

  /* Set SS bit and direction output from S720 to S1 */

  sam_write_reg(ILI9325_DRIVER_OUTPUT_CTRL1, ILI9325_DRIVER_OUTPUT_CTRL1_SS);

  /* Set 1 line inversion */

  sam_write_reg(ILI9325_LCD_DRIVING_CTRL,
                ILI9325_LCD_DRIVING_CTRL_BIT10 | ILI9325_LCD_DRIVING_CTRL_EOR |
                ILI9325_LCD_DRIVING_CTRL_BC0);

  /* Disable resizing feature */

  sam_write_reg(ILI9325_RESIZE_CTRL, 0);

  /* Set the back porch and front porch */

  sam_write_reg(ILI9325_DISP_CTRL2,
                ILI9325_DISP_CTRL2_BP(7) | ILI9325_DISP_CTRL2_FP(2));

  /* Set non-display area refresh cycle ISC[3:0] */

  sam_write_reg(ILI9325_DISP_CTRL3, 0);

  /* Disable FMARK function */

  sam_write_reg(ILI9325_DISP_CTRL4, 0);

  /* 18-bit RGB interface and writing display data by the system interface */

  sam_write_reg(ILI9325_RGB_DISP_INTERFACE_CTRL1, 0);

  /* Set the output position of frame cycle */

  sam_write_reg(ILI9325_FRAME_MAKER_SHIFT, 0);

  /* RGB interface polarity */

  sam_write_reg(ILI9325_RGB_DISP_INTERFACE_CTRL2, 0);

  /* Power on sequence **************************************************************/
  /* Disable sleep and standby mode */

  sam_write_reg(ILI9325_POWER_CTRL1, 0);

  /* Select the operating frequency of the step-up circuit 1,2 and set the
   * ratio factor of Vci
   */

  sam_write_reg(ILI9325_POWER_CTRL2, 0);

  /* Set VREG1OUT voltage */

  sam_write_reg(ILI9325_POWER_CTRL3, 0);

  /* Set VCOM amplitude */

  sam_write_reg(ILI9325_POWER_CTRL4, 0);
  up_mdelay(200);

  /* Enable power supply and source driver ******************************************/
  /* Adjust the constant current and set the factor used in the step-up
   * circuits.
   */

  sam_write_reg(ILI9325_POWER_CTRL1,
                ILI9325_POWER_CTRL1_SAP | ILI9325_POWER_CTRL1_BT(2) |
                ILI9325_POWER_CTRL1_APE | ILI9325_POWER_CTRL1_AP(1));

  /* Select the operating frequency of the step-up circuit 1,2 and set the
   * ratio factor of Vci
   */

  sam_write_reg(ILI9325_POWER_CTRL2,
                ILI9325_POWER_CTRL2_DC1(2) | ILI9325_POWER_CTRL2_DC0(2) |
                ILI9325_POWER_CTRL2_VC(7));
  up_mdelay(50);

  /* Internal reference voltage= Vci */

  sam_write_reg(ILI9325_POWER_CTRL3,
                ILI9325_POWER_CTRL3_PON | ILI9325_POWER_CTRL3_VRH(11));
  up_mdelay(50);

  /* Set VDV[4:0] for VCOM amplitude */

  sam_write_reg(ILI9325_POWER_CTRL4, ILI9325_POWER_CTRL4_VDV(17));

  /* Set VCM[5:0] for VCOMH */

  sam_write_reg(ILI9325_POWER_CTRL7, ILI9325_POWER_CTRL7_VCM(25));

  /* Set Frame Rate */

  sam_write_reg(ILI9325_FRAME_RATE_AND_COLOR_CTRL,
                ILI9325_FRAME_RATE_AND_COLOR_CTRL_FRS(13));
  up_mdelay(50);

  /* Adjust the Gamma Curve */

  sam_write_reg(ILI9325_GAMMA_CTRL1, 9);
  sam_write_reg(ILI9325_GAMMA_CTRL2,
                ILI9325_GAMMA_CTRL2_KP3(2) | ILI9325_GAMMA_CTRL2_KP2(4));
  sam_write_reg(ILI9325_GAMMA_CTRL3,
                ILI9325_GAMMA_CTRL3_KP5(2) | ILI9325_GAMMA_CTRL3_KP4(0));
  sam_write_reg(ILI9325_GAMMA_CTRL4,
                ILI9325_GAMMA_CTRL4_RP1(0) | ILI9325_GAMMA_CTRL4_RP0(7));
  sam_write_reg(ILI9325_GAMMA_CTRL5,
                ILI9325_GAMMA_CTRL5_VRP1(20) | ILI9325_GAMMA_CTRL5_VRP0(4));
  sam_write_reg(ILI9325_GAMMA_CTRL6,
                ILI9325_GAMMA_CTRL6_KN1(7) | ILI9325_GAMMA_CTRL6_KN0(5));
  sam_write_reg(ILI9325_GAMMA_CTRL7,
                ILI9325_GAMMA_CTRL7_KN3(3) | ILI9325_GAMMA_CTRL7_KN2(5));
  sam_write_reg(ILI9325_GAMMA_CTRL8,
                ILI9325_GAMMA_CTRL8_KN5(7) | ILI9325_GAMMA_CTRL8_KN4(7));
  sam_write_reg(ILI9325_GAMMA_CTRL9,
                ILI9325_GAMMA_CTRL9_RN1(7) | ILI9325_GAMMA_CTRL9_RN0(1));
  sam_write_reg(ILI9325_GAMMA_CTRL10,
                ILI9325_GAMMA_CTRL10_VRN1(0) | ILI9325_GAMMA_CTRL10_VRN0(14));

  /* Set the Entry Mode:
   *
   * AM controls the GRAM update direction.
   *   0 = The address is updated in horizontal writing direction.
   *   1 = The address is updated in vertical writing direction.
   *
   * I/D[1:0] controls the address counter (AC) to automatically increase or
   *   decrease by 1 when update one pixel display data.
   *
   *  00 = Horizontal decrement, Vertical decrement
   *  01 = Horizontal increment, Vertical decrement
   *  10 = Horizontal decrement, Vertical increment
   *  11 = Horizontal increment, Vertical increment
   *
   * ORG moves the origin address according to the ID setting when a window address
   * area is made.
   *   0 = The origin address is not moved.
   *   1 = The original address moves according to the I/D[1:0] setting.
   *
   * BGR swaps the R and B order of written data.
   *   0 = Follow the RGB order to write the pixel data.
   *   1 = Swap the RGB data to BGR in writing into GRAM.
   *
   * TRI = 1: Data are transferred to the internal RAM in 8-bit x 3 transfers mode
   *   via the 8-bit interface.
   *
   * DFM + TRI: Data is transferred as 3 byte transfers
   *
   * Use the high speed write mode (HWM=1).  When TRI = 1, data are transferred to
   * the internal RAM in 8-bit x 3 transfers mode via the 8-bit interface.  DFM set
   * the mode of transferring data to the internal RAM when TRI = 1.
   * I/D[1:0] = 11 Horizontal : increment Vertical : increment, AM=0:Horizontal
   */

#if defined(CONFIG_LCD_LANDSCAPE)
  /* Landscape: Horizontal increment/ Vertical decrement, address is update in
   * horizontal direction
   */

  regval = ILI9325_ENTRY_MODE_ID(1) | ILI9325_ENTRY_MODE_ORG | ILI9325_ENTRY_MODE_HWM |
           ILI9325_ENTRY_MODE_BGR | ILI9325_ENTRY_MODE_TRI | ILI9325_ENTRY_MODE_DFM;

#elif defined(CONFIG_LCD_RLANDSCAPE)
  /* Landscape: Horizontal decrement/ Vertical increment, address is update in
   * horizontal direction
   */

  regval = ILI9325_ENTRY_MODE_ID(2) | ILI9325_ENTRY_MODE_ORG | ILI9325_ENTRY_MODE_HWM |
           ILI9325_ENTRY_MODE_BGR | ILI9325_ENTRY_MODE_TRI | ILI9325_ENTRY_MODE_DFM;

#elif defined(CONFIG_LCD_PORTRAIT)
  /* Landscape: Horizontal decrement/ Vertical decrement, address is update in
   * vertical direction
   */

  regval = ILI9325_ENTRY_MODE_AM | ILI9325_ENTRY_MODE_ID(0) | ILI9325_ENTRY_MODE_ORG |
           ILI9325_ENTRY_MODE_HWM | ILI9325_ENTRY_MODE_BGR | ILI9325_ENTRY_MODE_TRI |
           ILI9325_ENTRY_MODE_DFM;

#else /* if defined(CONFIG_LCD_RPORTRAIT) */
  /* Landscape: Horizontal increment/ Vertical increment, address is update in
   * vertical direction
   */

  regval = ILI9325_ENTRY_MODE_AM | ILI9325_ENTRY_MODE_ID(3) | ILI9325_ENTRY_MODE_ORG |
           ILI9325_ENTRY_MODE_HWM | ILI9325_ENTRY_MODE_BGR | ILI9325_ENTRY_MODE_TRI |
           ILI9325_ENTRY_MODE_DFM;
#endif

  sam_write_reg(ILI9325_ENTRY_MODE, regval);

  /* Driver Output Control
   *
   * SS: Select the shift direction of outputs from the source driver
   *   0 = The shift direction of outputs is from S1 to S720
   *   1 = The shift direction of outputs is from S720 to S1
   * SM: Sets the gate driver pin arrangement in combination with the GS bit
   */

#if defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_RPORTRAIT)
  /* Horizontal increment */

  regval = 0;

#else /* defined(CONFIG_LCD_RLANDSCAPE) || defined(CONFIG_LCD_PORTRAIT) */
  /* Horizontal decrement */

  regval = ILI9325_DRIVER_OUTPUT_CTRL1_SS;
#endif

  sam_write_reg(ILI9325_DRIVER_OUTPUT_CTRL1, regval);

  /* Set the number of lines to drive the LCD at an interval of 8 lines. The scan
   * direction is from G320 to G1
   */

  regval = ILI9325_DRIVER_OUTPUT_CTRL2_NL((SAM_XRES / 8) - 1);

#if  defined(CONFIG_LCD_LANDSCAPE) || defined(CONFIG_LCD_PORTRAIT)
  /* Vertical decrement */

  regval |= ILI9325_DRIVER_OUTPUT_CTRL2_GS;
#endif

  sam_write_reg(ILI9325_DRIVER_OUTPUT_CTRL2, regval);

  /* Vertical Scrolling *************************************************************/
  /* Disable scrolling and enable the grayscale inversion */

  sam_write_reg(ILI9325_BASE_IMG_DISP_CTRL, ILI9325_BASE_IMG_DISP_CTRL_REV);
  sam_write_reg(ILI9325_VERTICAL_SCROLL_CTRL, 0);

  /* Disable Partial Display */

  sam_write_reg(ILI9325_PARTIAL_IMG1_DISP_SHIFT, 0);
  sam_write_reg(ILI9325_PARTIAL_IMG1_AREA_START_LINE, 0);
  sam_write_reg(ILI9325_PARTIAL_IMG1_AREA_END_LINE, 0);
  sam_write_reg(ILI9325_PARTIAL_IMG2_DISP_SHIFT, 0);
  sam_write_reg(ILI9325_PARTIAL_IMG2_AREA_START_LINE, 0);
  sam_write_reg(ILI9325_PARTIAL_IMG2_AREA_END_LINE, 0);

  /* Panel Control */

  sam_write_reg(ILI9325_PANEL_INTERFACE_CTRL1,
                ILI9325_PANEL_INTERFACE_CTRL1_RTNI(16));
  sam_write_reg(ILI9325_PANEL_INTERFACE_CTRL2,
               ILI9325_PANEL_INTERFACE_CTRL2_NOWI(6));
  sam_write_reg(ILI9325_PANEL_INTERFACE_CTRL4,
                ILI9325_PANEL_INTERFACE_CTRL4_DIVE(1) |
                ILI9325_PANEL_INTERFACE_CTRL4_RTNE(16));

  /* Set the drawing window */

  sam_write_reg(ILI9325_HORIZONTAL_ADDR_START, 0);
  sam_write_reg(ILI9325_HORIZONTAL_ADDR_END, SAM_XRES - 1);
  sam_write_reg(ILI9325_VERTICAL_ADDR_START, 0);
  sam_write_reg(ILI9325_VERTICAL_ADDR_END, SAM_YRES - 1);

  /* Home the cursor */

  sam_set_cursor(0, 0);
}

/************************************************************************************
 * Name:  sam_lcd_initialize
 *
 * Description:
 *   Initialize the LCD panel
 *
 ************************************************************************************/

static inline int sam_lcd_initialize(void)
{
  uint16_t id;

  /* Check the LCD ID */

  id = sam_read_reg(ILI9325_DEVICE_CODE_REG);
  lcdvdbg("LCD ID: %04x\n", id);

  /* Initialize the LCD hardware */

  if (id != ILI9325_DEVICE_CODE)
    {
      lcddbg("ERROR: Unsupported LCD: %04x\n", id);
      return -ENODEV;
    }

  sam_lcd9325_initialize();
  return OK;
}

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

/************************************************************************************
 * Name:  board_lcd_initialize
 *
 * 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).
 *
 ************************************************************************************/

int board_lcd_initialize(void)
{
  FAR struct sam_dev_s *priv = &g_lcddev;
  int ret;

  lcdvdbg("Initializing\n");

  /* Configure all LCD pins pins (backlight is initially off) */

  sam_gpio_initialize();

  /* Enable peripheral clock */

  sam_smc_enableclk();

  /* Configure SMC interface for the LCD */

  sam_smc_initialize();

  /* Identify and configure the LCD */

  up_mdelay(50);
  ret = sam_lcd_initialize();
  if (ret == OK)
    {
      /* Clear the display (setting it to the color 0=black) */

      sam_lcdclear(CONFIG_SAM4EEK_LCD_BGCOLOR);

      /* Turn the display off */

      sam_poweroff(priv);
    }

  return ret;
}

/************************************************************************************
 * Name: board_lcd_getdev
 *
 * Description:
 *   Return a a reference to the LCD object for the specified LCD.  This allows
 *   support for multiple LCD devices.
 *
 ************************************************************************************/

FAR struct lcd_dev_s *board_lcd_getdev(int lcddev)
{
  DEBUGASSERT(lcddev == 0);
  return &g_lcddev.dev;
}

/************************************************************************************
 * Name:  board_lcd_uninitialize
 *
 * Description:
 *   Uninitialize the LCD support
 *
 ************************************************************************************/

void board_lcd_uninitialize(void)
{
  FAR struct sam_dev_s *priv = &g_lcddev;

  /* Put the LCD in the lowest possible power state */

  sam_poweroff(priv);
}

/************************************************************************************
 * Name:  sam_lcdclear
 *
 * Description:
 *   This is a non-standard LCD interface just for the SAM4E-EK board.  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 GRAM memory to the specified color.
 *
 ************************************************************************************/

#if defined(CONFIG_SAM4EEK_LCD_RGB565)
void sam_lcdclear(uint16_t color)
#else /* if defined(CONFIG_SAM4EEK_LCD_RGB24) defined(CONFIG_SAM4EEK_LCD_RGB32) */
void sam_lcdclear(uint32_t color)
#endif
{
  uint8_t r = RGB_RED(color);
  uint8_t g = RGB_GREEN(color);
  uint8_t b = RGB_BLUE(color);
  uint32_t i;

  sam_set_cursor(0, 0);
  sam_gram_prepare();

  for (i = SAM_XRES * SAM_YRES; i > 0; i--)
    {
      LCD_DATA = r;
      LCD_DATA = g;
      LCD_DATA = b;
    }
}

#endif /* CONFIG_LCD */
