nuttx/drivers/lcd/ra8875.c

1514 lines
44 KiB
C
Raw Permalink Normal View History

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