text: /****************************************************************************
   * drivers/lcd/lpm013m091a.c
   *
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.  The
   * ASF licenses this file to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance with the
   * License.  You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
   * License for the specific language governing permissions and limitations
   * under the License.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <nuttx/config.h>
  
  #include <sys/types.h>
  #include <stdint.h>
  #include <stdbool.h>
  #include <string.h>
  #include <errno.h>
  #include <debug.h>
  
  #include <nuttx/arch.h>
  #include <nuttx/lcd/lcd.h>
  #include <nuttx/lcd/lpm013m091a.h>
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  /* Display resolution */
  
  #define LPM013M091A_XRES        320
  #define LPM013M091A_YRES        300
  
  /* TODO: Stride should be configurable by LCD orientation */
  
  #define LPM013M091A_STRIDE      LPM013M091A_XRES
  
  /* Dolor depth and format */
  
  #define LPM013M091A_BPP           16
  #define LPM013M091A_COLORFMT      FB_FMT_RGB16_565
  
  /****************************************************************************
   * Private types
   ****************************************************************************/
  
  struct lpm013m091a_dev_s
  {
    /* Publicly visible device structure */
  
    struct lcd_dev_s dev;
  
    /* Private lcd-specific information follows */
  
    struct lpm013m091a_lcd_s *lcd;
  
    uint8_t power;                  /* Current power setting */
  };
  
  /****************************************************************************
   * Private Function Protototypes
   ****************************************************************************/
  
  static void lpm013m091a_selectarea(FAR struct lpm013m091a_lcd_s *lcd,
                                     uint16_t x0, int16_t y0,
                                     uint16_t x1, int16_t y1);
  static int lpm013m091a_hwinitialize(FAR struct lpm013m091a_dev_s *dev);
  
  /* lcd data transfer methods */
  
  static int lpm013m091a_putrun(fb_coord_t row, fb_coord_t col,
                                FAR const uint8_t *buffer, size_t npixels);
  #ifndef CONFIG_LCD_NOGETRUN
  static int lpm013m091a_getrun(fb_coord_t row, fb_coord_t col,
                                FAR uint8_t *buffer,
                                size_t npixels);
  #endif
  
  /* lcd configuration */
  
  static int lpm013m091a_getvideoinfo(FAR struct lcd_dev_s *dev,
                                      FAR struct fb_videoinfo_s *vinfo);
  static int lpm013m091a_getplaneinfo(FAR struct lcd_dev_s *dev,
                                      unsigned int planeno,
                                      FAR struct lcd_planeinfo_s *pinfo);
  
  /* lcd specific controls */
  
  static int lpm013m091a_getpower(FAR struct lcd_dev_s *dev);
  static int lpm013m091a_setpower(FAR struct lcd_dev_s *dev, int power);
  static int lpm013m091a_getcontrast(FAR struct lcd_dev_s *dev);
  static int lpm013m091a_setcontrast(FAR struct lcd_dev_s *dev,
                                     unsigned int contrast);
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  static uint16_t g_runbuffer[LPM013M091A_STRIDE];
  
  /* This structure describes the overall lcd video controller */
  
  static const struct fb_videoinfo_s g_videoinfo =
  {
    .fmt = LPM013M091A_COLORFMT,            /* Color format: rgb16-565: rrrr rggg gggb bbbb */
    .xres = LPM013M091A_XRES,               /* Horizontal resolution in pixel columns */
    .yres = LPM013M091A_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 = lpm013m091a_putrun,           /* Put a run into lcd memory */
  #ifndef CONFIG_LCD_NOGETRUN
    .getrun = lpm013m091a_getrun,           /* Get a run from lcd memory */
  #endif
    .buffer = (uint8_t *) g_runbuffer,      /* Run scratch buffer */
    .bpp = LPM013M091A_BPP,                 /* Bits-per-pixel */
  };
  
  static struct lpm013m091a_dev_s g_lpm013m091a_dev =
  {
    .dev =
      {
        /* lcd configuration */
  
        .getvideoinfo = lpm013m091a_getvideoinfo,
        .getplaneinfo = lpm013m091a_getplaneinfo,
  
        /* lcd specific controls */
  
        .getpower = lpm013m091a_getpower,
        .setpower = lpm013m091a_setpower,
        .getcontrast = lpm013m091a_getcontrast,
        .setcontrast = lpm013m091a_setcontrast,
      },
    .lcd = 0,
  };
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name:  lpm013m091a_selectarea
   *
   * Description:
   *   Select the active area for displaying pixel
   *
   * Parameter:
   *   lcd       - Reference to private driver structure
   *   x0        - Start x position
   *   y0        - Start y position
   *   x1        - End x position
   *   y1        - End y position
   *
   ****************************************************************************/
  
  static void lpm013m091a_selectarea(FAR struct lpm013m091a_lcd_s *lcd,
                                     uint16_t x0, int16_t y0,
                                     uint16_t x1, int16_t y1)
  {
    lcd->sendcmd(lcd, LPM013M091A_CASET);
    lcd->sendparam(lcd, x0 >> 8);
    lcd->sendparam(lcd, x0 & 0xff);
    lcd->sendparam(lcd, x1 >> 8);
    lcd->sendparam(lcd, x1 & 0xff);
  
    lcd->sendcmd(lcd, LPM013M091A_PASET);
    lcd->sendparam(lcd, y0 >> 8);
    lcd->sendparam(lcd, y0 & 0xff);
    lcd->sendparam(lcd, y1 >> 8);
    lcd->sendparam(lcd, y1 & 0xff);
  }
  
  /****************************************************************************
   * Name:  lpm013m091a_hwinitialize
   *
   * Description:
   *   Initialize and configure the LPM013M091A LCD driver hardware.
   *
   * Parameter:
   *   dev - A reference to the driver specific structure
   *
   * Returned Value:
   *
   *   On success - OK
   *   On error - EINVAL
   *
   ****************************************************************************/
  
  static int lpm013m091a_hwinitialize(FAR struct lpm013m091a_dev_s *dev)
  {
    FAR struct lpm013m091a_lcd_s *lcd = dev->lcd;
  
    /* Soft reset */
  
    lcd->sendcmd(lcd, LPM013M091A_SWRESET);
    up_mdelay(10);
  
    /* Analog mode */
  
    lcd->sendcmd(lcd, 0xb3);
    lcd->sendparam(lcd, 0x02);
  
    /* Set Display Mode */
  
    lcd->sendcmd(lcd, 0xbb);
    lcd->sendparam(lcd, 0x10);
  
    /* SPI GRAM access enable */
  
    lcd->sendcmd(lcd, 0xf3);
    lcd->sendparam(lcd, 0x02);
  
    /* Bright Level Max */
  
    lcd->sendcmd(lcd, 0x51);
    lcd->sendparam(lcd, 0xff);
  
    /* Backlight ON */
  
    lcd->sendcmd(lcd, 0x53);
    lcd->sendparam(lcd, 0x24);
  
    /* Frame rate 60Hz */
  
    lcd->sendcmd(lcd, 0xff);
    lcd->sendparam(lcd, 0x24);
    lcd->sendcmd(lcd, 0xd8);
    lcd->sendparam(lcd, 0x41);
    lcd->sendcmd(lcd, 0xd9);
    lcd->sendparam(lcd, 0x1e);
  
    lcd->sendcmd(lcd, 0xff);
    lcd->sendparam(lcd, 0x10);
  
    /* Set the color format (18bit:0x06, 16bit:0x05) */
  
    lcd->sendcmd(lcd, LPM013M091A_PIXFMT);
    lcd->sendparam(lcd, 0x05);
  
    /* Sleep out */
  
    lcd->sendcmd(lcd, LPM013M091A_SLPOUT);
    up_mdelay(10);
  
    /* Display on */
  
    lcd->sendcmd(lcd, LPM013M091A_DISPON);
    up_mdelay(120);
  
    return OK;
  }
  
  /****************************************************************************
   * Name:  lpm013m091a_putrun
   *
   * Description:
   *   Write a partial raster line to the LCD.
   *
   * Parameters:
   *   devno   - Number of 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
   *             (range: 0 < npixels <= xres-col)
   *
   * Returned Value:
   *
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int lpm013m091a_putrun(fb_coord_t row, fb_coord_t col,
                                FAR const uint8_t *buffer, size_t npixels)
  {
    FAR struct lpm013m091a_dev_s *dev = (FAR struct lpm013m091a_dev_s *)
                                         &g_lpm013m091a_dev;
    FAR struct lpm013m091a_lcd_s *lcd = dev->lcd;
    FAR const uint16_t *src = (FAR const uint16_t *)buffer;
  
    DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0);
  
    /* Check if position outside of area */
  
    if (col + npixels > LPM013M091A_XRES || row > LPM013M091A_YRES)
      {
        return -EINVAL;
      }
  
    /* Select lcd driver */
  
    lcd->select(lcd);
  
    /* Select column and area similar to the partial raster line */
  
    lpm013m091a_selectarea(lcd, col, row, col + npixels - 1, row);
  
    /* Send memory write cmd */
  
    lcd->sendcmd(lcd, LPM013M091A_RAMWR);
  
    /* Send pixel to gram */
  
    lcd->sendgram(lcd, src, npixels);
  
    /* Deselect the lcd driver */
  
    lcd->deselect(lcd);
  
    return OK;
  }
  
  /****************************************************************************
   * Name:  lpm013m091a_getrun
   *
   * Description:
   *   Read a partial raster line from the LCD.
   *
   * Parameter:
   *   devno   - Number of 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)
   *
   * Returned Value:
   *
   *   On success - OK
   *   On error   - -EINVAL
   *
   ****************************************************************************/
  
  #ifndef CONFIG_LCD_NOGETRUN
  int lpm013m091a_getrun(fb_coord_t row, fb_coord_t col, FAR uint8_t * buffer,
                         size_t npixels)
  {
    lcderr("getrun is not supported for now.\n");
    return -ENOSYS;
  }
  #endif
  
  /****************************************************************************
   * Name:  lpm013m091a_getvideoinfo
   *
   * Description:
   *   Get information about the LCD video controller configuration.
   *
   * Parameter:
   *   dev - A reference to the driver specific structure
   *   vinfo - A reference to the videoinfo structure
   *
   * Returned Value:
   *
   *  On success - OK
   *  On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int lpm013m091a_getvideoinfo(FAR struct lcd_dev_s *dev,
                                      FAR struct fb_videoinfo_s *vinfo)
  {
    if (dev && vinfo)
      {
        memcpy(vinfo, &g_videoinfo, sizeof(struct fb_videoinfo_s));
  
        lcdinfo("fmt: %d xres: %d yres: %d nplanes: %d\n",
                vinfo->fmt, vinfo->xres, vinfo->yres, vinfo->nplanes);
  
        return OK;
      }
  
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name:  lpm013m091a_getplaneinfo
   *
   * Description:
   *   Get information about the configuration of each LCD color plane.
   *
   * Parameter:
   *   dev     - A reference to the driver specific structure
   *   planeno - The plane number
   *   pinfo   - A reference to the planeinfo structure
   *
   * Returned Value:
   *
   *  On success - OK
   *  On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int lpm013m091a_getplaneinfo(FAR struct lcd_dev_s *dev,
                                      unsigned int planeno,
                                      FAR struct lcd_planeinfo_s *pinfo)
  {
    if (dev && pinfo && planeno == 0)
      {
        memcpy(pinfo, &g_planeinfo, sizeof(struct lcd_planeinfo_s));
  
        lcdinfo("planeno: %d bpp: %d\n", planeno, pinfo->bpp);
  
        return OK;
      }
  
    return -EINVAL;
  }
  
  /****************************************************************************
   * Name:  lpm013m091a_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.
   *
   * Parameter:
   *   dev     - A reference to the driver specific structure
   *
   * Returned Value:
   *
   *  On success - OK
   *  On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int lpm013m091a_getpower(FAR struct lcd_dev_s *dev)
  {
    FAR struct lpm013m091a_dev_s *priv = (FAR struct lpm013m091a_dev_s *)dev;
  
    lcdinfo("%d\n", priv->power);
    return priv->power;
  }
  
  /****************************************************************************
   * Name:  lpm013m091a_setpower
   *
   * Description:
   *   Enable/disable LCD panel power
   *  (0: full off - CONFIG_LCD_MAXPOWER: full on).
   *   On backlight LCDs, this setting may correspond to the backlight setting.
   *
   * Parameter:
   *   dev   - A reference to the driver specific structure
   *   power - Value of the power
   *
   * Returned Value:
   *
   *  On success - OK
   *  On error   - -EINVAL
   *
   ****************************************************************************/
  
  static int lpm013m091a_setpower(FAR struct lcd_dev_s *dev, int power)
  {
    FAR struct lpm013m091a_dev_s *priv = (FAR struct lpm013m091a_dev_s *)dev;
    FAR struct lpm013m091a_lcd_s *lcd = priv->lcd;
  
    if (!dev)
      {
        return -EINVAL;
      }
  
    lcdinfo("%d\n", power);
  
    lcd->select(lcd);
  
    if (power > 0)
      {
        lcd->backlight(lcd, power);
  
        lcd->sendcmd(lcd, LPM013M091A_DISPON);
        up_mdelay(120);
      }
    else
      {
        lcd->sendcmd(lcd, LPM013M091A_DISPOFF);
      }
  
    lcd->deselect(lcd);
  
    priv->power = power;
  
    return OK;
  }
  
  /****************************************************************************
   * Name:  ili9340_getcontrast
   *
   * Description:
   *   Get the current contrast setting (0-CONFIG_LCD_MAXCONTRAST).
   *
   * Parameter:
   *   dev   - A reference to the lcd driver structure
   *
   * Returned Value:
   *
   *  On success - current contrast value
   *  On error   - -ENOSYS, not supported by the ili9340.
   *
   ****************************************************************************/
  
  static int lpm013m091a_getcontrast(FAR struct lcd_dev_s *dev)
  {
    lcdinfo("Not implemented\n");
    return -ENOSYS;
  }
  
  /****************************************************************************
   * Name:  ili9340_setcontrast
   *
   * Description:
   *   Set LCD panel contrast (0-CONFIG_LCD_MAXCONTRAST).
   *
   * Parameter:
   *   dev   - A reference to the lcd driver structure
   *
   * Returned Value:
   *
   *  On success - OK
   *  On error   - -ENOSYS, not supported by the ili9340.
   *
   ****************************************************************************/
  
  static int lpm013m091a_setcontrast(FAR struct lcd_dev_s *dev,
                                     unsigned int contrast)
  {
    lcdinfo("Not implemented\n");
    return -ENOSYS;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Initialize LCD
   ****************************************************************************/
  
  FAR struct lcd_dev_s *
    lpm013m091a_initialize(FAR struct lpm013m091a_lcd_s *lcd, int devno)
  {
    FAR struct lpm013m091a_dev_s *priv = &g_lpm013m091a_dev;
  
    if (lcd && devno == 0)
      {
        if (!priv->lcd)
          {
            FAR struct lcd_dev_s *dev = &priv->dev;
            int   ret;
  
            /* Initialize internal structure */
  
            dev->getvideoinfo = lpm013m091a_getvideoinfo;
            dev->getplaneinfo = lpm013m091a_getplaneinfo;
            dev->getpower     = lpm013m091a_getpower;
            dev->setpower     = lpm013m091a_setpower;
            dev->getcontrast  = lpm013m091a_getcontrast;
            dev->setcontrast  = lpm013m091a_setcontrast;
            priv->lcd         = lcd;
  
            /* Initialize the LCD driver */
  
            ret = lpm013m091a_hwinitialize(priv);
  
            if (ret == OK)
              {
                return &priv->dev;
              }
          }
      }
  
    return NULL;
  }
  