drivers: display: ili9xxx: generalize ILI display driver

Make driver generic for multiple ILI displays. The adopted strategy is
to share all driver code except register initialization, which has been
found to have some specific registers/values depending on the
controller.

The driver has been adjusted to support multiple compatibles.

Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
This commit is contained in:
Gerard Marull-Paretas 2020-10-26 19:20:16 +01:00 committed by Maureen Helm
commit acb0cd65ca
13 changed files with 735 additions and 629 deletions

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <dt-bindings/display/ili9340.h>
#include <dt-bindings/display/ili9xxx.h>
&arduino_spi {
status = "okay";
@ -17,7 +17,7 @@
spi-max-frequency = <15151515>;
reg = <0>;
cmd-data-gpios = <&arduino_header 15 GPIO_ACTIVE_LOW>; /* D9 */
pixel-format = <ILI9340_PIXEL_FORMAT_RGB888>;
pixel-format = <ILI9XXX_PIXEL_FORMAT_RGB888>;
rotation = <90>;
frmctr1 = [00 18];
pwctrl1 = [23 00];

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <dt-bindings/display/ili9340.h>
#include <dt-bindings/display/ili9xxx.h>
&arduino_spi {
status = "okay";
@ -17,7 +17,7 @@
reg = <0>;
cmd-data-gpios = <&arduino_header 13 GPIO_ACTIVE_LOW>; /* D7 */
reset-gpios = <&arduino_header 16 GPIO_ACTIVE_LOW>; /* D10 */
pixel-format = <ILI9340_PIXEL_FORMAT_RGB565>;
pixel-format = <ILI9XXX_PIXEL_FORMAT_RGB565>;
rotation = <0>;
frmctr1 = [00 18];
pwctrl1 = [23 00];

View file

@ -7,10 +7,14 @@ zephyr_sources_ifdef(CONFIG_SSD16XX ssd16xx.c)
zephyr_sources_ifdef(CONFIG_SDL_DISPLAY display_sdl.c)
zephyr_sources_ifdef(CONFIG_DUMMY_DISPLAY display_dummy.c)
zephyr_sources_ifdef(CONFIG_FRAMEBUF_DISPLAY display_framebuf.c)
zephyr_sources_ifdef(CONFIG_ILI9340 display_ili9340.c)
zephyr_sources_ifdef(CONFIG_ST7789V display_st7789v.c)
zephyr_sources_ifdef(CONFIG_GD7965 gd7965.c)
if (CONFIG_ILI9XXX)
zephyr_sources(display_ili9xxx.c)
endif()
zephyr_sources_ifdef(CONFIG_ILI9340 display_ili9340.c)
zephyr_sources_ifdef(CONFIG_MICROBIT_DISPLAY
mb_display.c
mb_font.c

View file

@ -17,7 +17,7 @@ source "subsys/logging/Kconfig.template.log_config"
source "drivers/display/Kconfig.grove"
source "drivers/display/Kconfig.mcux_elcdif"
source "drivers/display/Kconfig.microbit"
source "drivers/display/Kconfig.ili9340"
source "drivers/display/Kconfig.ili9xxx"
source "drivers/display/Kconfig.sdl"
source "drivers/display/Kconfig.ssd1306"
source "drivers/display/Kconfig.ssd16xx"

View file

@ -1,10 +1,16 @@
# ILI9340 display driver configuration options
# ILI9XXX display driver configuration options
# Copyright (c) 2017 Jan Van Winkel <jan.van_winkel@dxplore.eu>
# SPDX-License-Identifier: Apache-2.0
config ILI9XXX
bool
help
Hidden configuration entry for all ILI9XXX drivers.
config ILI9340
bool "ILI9340 display driver"
depends on SPI
select ILI9XXX
help
Enable driver for ILI9340 display driver.

View file

@ -1,561 +1,84 @@
/*
* Copyright (c) 2017 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright (c) 2019 Nordic Semiconductor ASA
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ilitek_ili9340
#include "display_ili9340.h"
#include <string.h>
#include <dt-bindings/display/ili9340.h>
#include <drivers/display.h>
#include <drivers/gpio.h>
#include <drivers/spi.h>
#include <sys/byteorder.h>
#include "display_ili9xxx.h"
#include <logging/log.h>
LOG_MODULE_REGISTER(display_ili9340, CONFIG_DISPLAY_LOG_LEVEL);
struct ili9340_config {
const char *spi_name;
uint16_t spi_addr;
uint32_t spi_max_freq;
const char *spi_cs_label;
gpio_pin_t spi_cs_pin;
gpio_dt_flags_t spi_cs_flags;
const char *cmd_data_label;
gpio_pin_t cmd_data_pin;
gpio_dt_flags_t cmd_data_flags;
const char *reset_label;
gpio_pin_t reset_pin;
gpio_dt_flags_t reset_flags;
uint8_t pixel_format;
uint16_t rotation;
uint8_t gamset[ILI9340_GAMSET_LEN];
uint8_t frmctr1[ILI9340_FRMCTR1_LEN];
uint8_t disctrl[ILI9340_DISCTRL_LEN];
uint8_t pwctrl1[ILI9340_PWCTRL1_LEN];
uint8_t pwctrl2[ILI9340_PWCTRL2_LEN];
uint8_t vmctrl1[ILI9340_VMCTRL1_LEN];
uint8_t vmctrl2[ILI9340_VMCTRL2_LEN];
uint8_t pgamctrl[ILI9340_PGAMCTRL_LEN];
uint8_t ngamctrl[ILI9340_NGAMCTRL_LEN];
};
struct ili9340_data {
const struct device *reset_gpio;
const struct device *command_data_gpio;
const struct device *spi_dev;
struct spi_config spi_config;
struct spi_cs_control cs_ctrl;
uint8_t bytes_per_pixel;
enum display_pixel_format pixel_format;
enum display_orientation orientation;
};
static int ili9340_transmit(const struct device *dev, uint8_t cmd,
const void *tx_data, size_t tx_len)
int ili9340_regs_init(const struct device *dev)
{
const struct ili9340_config *config = (struct ili9340_config *)dev->config;
struct ili9340_data *data = (struct ili9340_data *)dev->data;
const struct ili9xxx_config *config = dev->config;
const struct ili9340_regs *regs = config->regs;
int r;
struct spi_buf tx_buf;
struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1U };
/* send command */
tx_buf.buf = &cmd;
tx_buf.len = 1U;
gpio_pin_set(data->command_data_gpio, config->cmd_data_pin, ILI9340_CMD);
r = spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
LOG_HEXDUMP_DBG(regs->gamset, ILI9340_GAMSET_LEN, "GAMSET");
r = ili9xxx_transmit(dev, ILI9340_GAMSET, regs->gamset,
ILI9340_GAMSET_LEN);
if (r < 0) {
return r;
}
/* send data (if any) */
if (tx_data != NULL) {
tx_buf.buf = (void *)tx_data;
tx_buf.len = tx_len;
gpio_pin_set(data->command_data_gpio, config->cmd_data_pin, ILI9340_DATA);
r = spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
if (r < 0) {
return r;
}
}
return 0;
}
static int ili9340_exit_sleep(const struct device *dev)
{
int r;
r = ili9340_transmit(dev, ILI9340_SLPOUT, NULL, 0);
LOG_HEXDUMP_DBG(regs->frmctr1, ILI9340_FRMCTR1_LEN, "FRMCTR1");
r = ili9xxx_transmit(dev, ILI9340_FRMCTR1, regs->frmctr1,
ILI9340_FRMCTR1_LEN);
if (r < 0) {
return r;
}
k_sleep(K_MSEC(ILI9340_SLEEP_OUT_TIME));
return 0;
}
static void ili9340_hw_reset(const struct device *dev)
{
const struct ili9340_config *config = (struct ili9340_config *)dev->config;
struct ili9340_data *data = (struct ili9340_data *)dev->data;
if (data->reset_gpio == NULL) {
return;
}
gpio_pin_set(data->reset_gpio, config->reset_pin, 1);
k_sleep(K_MSEC(ILI9340_RESET_PULSE_TIME));
gpio_pin_set(data->reset_gpio, config->reset_pin, 0);
k_sleep(K_MSEC(ILI9340_RESET_WAIT_TIME));
}
static int ili9340_set_mem_area(const struct device *dev, const uint16_t x,
const uint16_t y, const uint16_t w, const uint16_t h)
{
int r;
uint16_t spi_data[2];
spi_data[0] = sys_cpu_to_be16(x);
spi_data[1] = sys_cpu_to_be16(x + w - 1U);
r = ili9340_transmit(dev, ILI9340_CASET, &spi_data[0], 4U);
LOG_HEXDUMP_DBG(regs->disctrl, ILI9340_DISCTRL_LEN, "DISCTRL");
r = ili9xxx_transmit(dev, ILI9340_DISCTRL, regs->disctrl,
ILI9340_DISCTRL_LEN);
if (r < 0) {
return r;
}
spi_data[0] = sys_cpu_to_be16(y);
spi_data[1] = sys_cpu_to_be16(y + h - 1U);
r = ili9340_transmit(dev, ILI9340_PASET, &spi_data[0], 4U);
LOG_HEXDUMP_DBG(regs->pwctrl1, ILI9340_PWCTRL1_LEN, "PWCTRL1");
r = ili9xxx_transmit(dev, ILI9340_PWCTRL1, regs->pwctrl1,
ILI9340_PWCTRL1_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(regs->pwctrl2, ILI9340_PWCTRL2_LEN, "PWCTRL2");
r = ili9xxx_transmit(dev, ILI9340_PWCTRL2, regs->pwctrl2,
ILI9340_PWCTRL2_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(regs->vmctrl1, ILI9340_VMCTRL1_LEN, "VMCTRL1");
r = ili9xxx_transmit(dev, ILI9340_VMCTRL1, regs->vmctrl1,
ILI9340_VMCTRL1_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(regs->vmctrl2, ILI9340_VMCTRL2_LEN, "VMCTRL2");
r = ili9xxx_transmit(dev, ILI9340_VMCTRL2, regs->vmctrl2,
ILI9340_VMCTRL2_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(regs->pgamctrl, ILI9340_PGAMCTRL_LEN, "PGAMCTRL");
r = ili9xxx_transmit(dev, ILI9340_PGAMCTRL, regs->pgamctrl,
ILI9340_PGAMCTRL_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(regs->ngamctrl, ILI9340_NGAMCTRL_LEN, "NGAMCTRL");
r = ili9xxx_transmit(dev, ILI9340_NGAMCTRL, regs->ngamctrl,
ILI9340_NGAMCTRL_LEN);
if (r < 0) {
return r;
}
return 0;
}
static int ili9340_write(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
const void *buf)
{
struct ili9340_data *data = (struct ili9340_data *)dev->data;
int r;
const uint8_t *write_data_start = (const uint8_t *)buf;
struct spi_buf tx_buf;
struct spi_buf_set tx_bufs;
uint16_t write_cnt;
uint16_t nbr_of_writes;
uint16_t write_h;
__ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
__ASSERT((desc->pitch * data->bytes_per_pixel * desc->height) <= desc->buf_size,
"Input buffer to small");
LOG_DBG("Writing %dx%d (w,h) @ %dx%d (x,y)", desc->width, desc->height,
x, y);
r = ili9340_set_mem_area(dev, x, y, desc->width, desc->height);
if (r < 0) {
return r;
}
if (desc->pitch > desc->width) {
write_h = 1U;
nbr_of_writes = desc->height;
} else {
write_h = desc->height;
nbr_of_writes = 1U;
}
r = ili9340_transmit(dev, ILI9340_RAMWR, write_data_start,
desc->width * data->bytes_per_pixel * write_h);
if (r < 0) {
return r;
}
tx_bufs.buffers = &tx_buf;
tx_bufs.count = 1;
write_data_start += desc->pitch * data->bytes_per_pixel;
for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) {
tx_buf.buf = (void *)write_data_start;
tx_buf.len = desc->width * data->bytes_per_pixel * write_h;
r = spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
if (r < 0) {
return r;
}
write_data_start += desc->pitch * data->bytes_per_pixel;
}
return 0;
}
static int ili9340_read(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
void *buf)
{
LOG_ERR("Reading not supported");
return -ENOTSUP;
}
static void *ili9340_get_framebuffer(const struct device *dev)
{
LOG_ERR("Direct framebuffer access not supported");
return NULL;
}
static int ili9340_display_blanking_off(const struct device *dev)
{
LOG_DBG("Turning display blanking off");
return ili9340_transmit(dev, ILI9340_DISPON, NULL, 0);
}
static int ili9340_display_blanking_on(const struct device *dev)
{
LOG_DBG("Turning display blanking on");
return ili9340_transmit(dev, ILI9340_DISPOFF, NULL, 0);
}
static int ili9340_set_brightness(const struct device *dev,
const uint8_t brightness)
{
LOG_ERR("Set brightness not implemented");
return -ENOTSUP;
}
static int ili9340_set_contrast(const struct device *dev, const uint8_t contrast)
{
LOG_ERR("Set contrast not supported");
return -ENOTSUP;
}
static int ili9340_set_pixel_format(const struct device *dev,
const enum display_pixel_format
pixel_format)
{
struct ili9340_data *data = (struct ili9340_data *)dev->data;
int r;
uint8_t tx_data;
uint8_t bytes_per_pixel;
if (pixel_format == PIXEL_FORMAT_RGB_565) {
bytes_per_pixel = 2U;
tx_data = ILI9340_PIXSET_MCU_16_BIT | ILI9340_PIXSET_RGB_16_BIT;
} else if (pixel_format == PIXEL_FORMAT_RGB_888) {
bytes_per_pixel = 3U;
tx_data = ILI9340_PIXSET_MCU_18_BIT | ILI9340_PIXSET_RGB_18_BIT;
} else {
LOG_ERR("Unsupported pixel format");
return -ENOTSUP;
}
r = ili9340_transmit(dev, ILI9340_PIXSET, &tx_data, 1U);
if (r < 0) {
return r;
}
data->pixel_format = pixel_format;
data->bytes_per_pixel = bytes_per_pixel;
return 0;
}
static int ili9340_set_orientation(const struct device *dev,
const enum display_orientation orientation)
{
struct ili9340_data *data = (struct ili9340_data *)dev->data;
int r;
uint8_t tx_data = ILI9340_MADCTL_BGR;
if (orientation == DISPLAY_ORIENTATION_NORMAL) {
tx_data |= ILI9340_MADCTL_MX;
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
tx_data |= ILI9340_MADCTL_MV;
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
tx_data |= ILI9340_MADCTL_MY;
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
tx_data |= ILI9340_MADCTL_MV | ILI9340_MADCTL_MX | ILI9340_MADCTL_MY;
}
r = ili9340_transmit(dev, ILI9340_MADCTL, &tx_data, 1U);
if (r < 0) {
return r;
}
data->orientation = orientation;
return 0;
}
static void ili9340_get_capabilities(const struct device *dev,
struct display_capabilities *capabilities)
{
struct ili9340_data *data = (struct ili9340_data *)dev->data;
memset(capabilities, 0, sizeof(struct display_capabilities));
capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_565 |
PIXEL_FORMAT_RGB_888;
capabilities->current_pixel_format = data->pixel_format;
if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
capabilities->x_resolution = ILI9340_X_RES;
capabilities->y_resolution = ILI9340_Y_RES;
} else {
capabilities->x_resolution = ILI9340_Y_RES;
capabilities->y_resolution = ILI9340_X_RES;
}
capabilities->current_orientation = data->orientation;
}
static int ili9340_configure(const struct device *dev)
{
const struct ili9340_config *config = (struct ili9340_config *)dev->config;
int r;
enum display_pixel_format pixel_format;
enum display_orientation orientation;
uint8_t tx_data[15];
/* pixel format */
if (config->pixel_format == ILI9340_PIXEL_FORMAT_RGB565) {
pixel_format = PIXEL_FORMAT_RGB_565;
} else {
pixel_format = PIXEL_FORMAT_RGB_888;
}
r = ili9340_set_pixel_format(dev, pixel_format);
if (r < 0) {
return r;
}
/* orientation */
if (config->rotation == 0U) {
orientation = DISPLAY_ORIENTATION_NORMAL;
} else if (config->rotation == 90U) {
orientation = DISPLAY_ORIENTATION_ROTATED_90;
} else if (config->rotation == 180U) {
orientation = DISPLAY_ORIENTATION_ROTATED_180;
} else {
orientation = DISPLAY_ORIENTATION_ROTATED_270;
}
r = ili9340_set_orientation(dev, orientation);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->gamset, ILI9340_GAMSET_LEN, "GAMSET");
memcpy(tx_data, config->gamset, ILI9340_GAMSET_LEN);
r = ili9340_transmit(dev, ILI9340_GAMSET, tx_data, ILI9340_GAMSET_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->frmctr1, ILI9340_FRMCTR1_LEN, "FRMCTR1");
memcpy(tx_data, config->frmctr1, ILI9340_FRMCTR1_LEN);
r = ili9340_transmit(dev, ILI9340_FRMCTR1, tx_data, ILI9340_FRMCTR1_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->disctrl, ILI9340_DISCTRL_LEN, "DISCTRL");
memcpy(tx_data, config->disctrl, ILI9340_DISCTRL_LEN);
r = ili9340_transmit(dev, ILI9340_DISCTRL, tx_data, ILI9340_DISCTRL_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->pwctrl1, ILI9340_PWCTRL1_LEN, "PWCTRL1");
memcpy(tx_data, config->pwctrl1, ILI9340_PWCTRL1_LEN);
r = ili9340_transmit(dev, ILI9340_PWCTRL1, tx_data, ILI9340_PWCTRL1_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->pwctrl2, ILI9340_PWCTRL2_LEN, "PWCTRL2");
memcpy(tx_data, config->pwctrl2, ILI9340_PWCTRL2_LEN);
r = ili9340_transmit(dev, ILI9340_PWCTRL2, tx_data, ILI9340_PWCTRL2_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->vmctrl1, ILI9340_VMCTRL1_LEN, "VMCTRL1");
memcpy(tx_data, config->vmctrl1, ILI9340_VMCTRL1_LEN);
r = ili9340_transmit(dev, ILI9340_VMCTRL1, tx_data, ILI9340_VMCTRL1_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->vmctrl2, ILI9340_VMCTRL2_LEN, "VMCTRL2");
memcpy(tx_data, config->vmctrl2, ILI9340_VMCTRL2_LEN);
r = ili9340_transmit(dev, ILI9340_VMCTRL2, tx_data, ILI9340_VMCTRL2_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->pgamctrl, ILI9340_PGAMCTRL_LEN, "PGAMCTRL");
memcpy(tx_data, config->pgamctrl, ILI9340_PGAMCTRL_LEN);
r = ili9340_transmit(dev, ILI9340_PGAMCTRL, tx_data, ILI9340_PGAMCTRL_LEN);
if (r < 0) {
return r;
}
LOG_HEXDUMP_DBG(config->ngamctrl, ILI9340_NGAMCTRL_LEN, "NGAMCTRL");
memcpy(tx_data, config->ngamctrl, ILI9340_NGAMCTRL_LEN);
r = ili9340_transmit(dev, ILI9340_NGAMCTRL, tx_data, ILI9340_NGAMCTRL_LEN);
if (r < 0) {
return r;
}
return 0;
}
static int ili9340_init(const struct device *dev)
{
const struct ili9340_config *config = (struct ili9340_config *)dev->config;
struct ili9340_data *data = (struct ili9340_data *)dev->data;
int r;
data->spi_dev = device_get_binding(config->spi_name);
if (data->spi_dev == NULL) {
LOG_ERR("Could not get SPI device %s", config->spi_name);
return -ENODEV;
}
data->spi_config.frequency = config->spi_max_freq;
data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8U);
data->spi_config.slave = config->spi_addr;
data->cs_ctrl.gpio_dev = device_get_binding(config->spi_cs_label);
if (data->cs_ctrl.gpio_dev != NULL) {
data->cs_ctrl.gpio_pin = config->spi_cs_pin;
data->cs_ctrl.gpio_dt_flags = config->spi_cs_flags;
data->cs_ctrl.delay = 0U;
data->spi_config.cs = &data->cs_ctrl;
}
data->command_data_gpio = device_get_binding(config->cmd_data_label);
if (data->command_data_gpio == NULL) {
LOG_ERR("Could not get command/data GPIO port %s", config->cmd_data_label);
return -ENODEV;
}
r = gpio_pin_configure(data->command_data_gpio, config->cmd_data_pin,
GPIO_OUTPUT | config->cmd_data_flags);
if (r < 0) {
LOG_ERR("Could not configure command/data GPIO (%d)", r);
return r;
}
data->reset_gpio = device_get_binding(config->reset_label);
if (data->reset_gpio != NULL) {
r = gpio_pin_configure(data->reset_gpio, config->reset_pin,
GPIO_OUTPUT_INACTIVE | config->reset_flags);
if (r < 0) {
LOG_ERR("Could not configure reset GPIO (%d)", r);
return r;
}
}
ili9340_hw_reset(dev);
r = ili9340_configure(dev);
if (r < 0) {
LOG_ERR("Could not configure display (%d)", r);
return r;
}
r = ili9340_exit_sleep(dev);
if (r < 0) {
LOG_ERR("Could not exit sleep mode (%d)", r);
return r;
}
return 0;
}
static const struct display_driver_api ili9340_api = {
.blanking_on = ili9340_display_blanking_on,
.blanking_off = ili9340_display_blanking_off,
.write = ili9340_write,
.read = ili9340_read,
.get_framebuffer = ili9340_get_framebuffer,
.set_brightness = ili9340_set_brightness,
.set_contrast = ili9340_set_contrast,
.get_capabilities = ili9340_get_capabilities,
.set_pixel_format = ili9340_set_pixel_format,
.set_orientation = ili9340_set_orientation,
};
#define ILI9340_INIT(index) \
static const struct ili9340_config ili9340_config_##index = { \
.spi_name = DT_INST_BUS_LABEL(index), \
.spi_addr = DT_INST_REG_ADDR(index), \
.spi_max_freq = UTIL_AND( \
DT_INST_HAS_PROP(index, spi_max_frequency), \
DT_INST_PROP(index, spi_max_frequency) \
), \
.spi_cs_label = UTIL_AND( \
DT_INST_SPI_DEV_HAS_CS_GPIOS(index), \
DT_INST_SPI_DEV_CS_GPIOS_LABEL(index)), \
.spi_cs_pin = UTIL_AND( \
DT_INST_SPI_DEV_HAS_CS_GPIOS(index), \
DT_INST_SPI_DEV_CS_GPIOS_PIN(index)), \
.spi_cs_flags = UTIL_AND( \
DT_INST_SPI_DEV_HAS_CS_GPIOS(index), \
DT_INST_SPI_DEV_CS_GPIOS_FLAGS(index)), \
.cmd_data_label = DT_INST_GPIO_LABEL(index, cmd_data_gpios), \
.cmd_data_pin = DT_INST_GPIO_PIN(index, cmd_data_gpios), \
.cmd_data_flags = DT_INST_GPIO_FLAGS(index, cmd_data_gpios), \
.reset_label = UTIL_AND( \
DT_INST_NODE_HAS_PROP(index, reset_gpios), \
DT_INST_GPIO_LABEL(index, reset_gpios)), \
.reset_pin = UTIL_AND( \
DT_INST_NODE_HAS_PROP(index, reset_gpios), \
DT_INST_GPIO_PIN(index, reset_gpios)), \
.reset_flags = UTIL_AND( \
DT_INST_NODE_HAS_PROP(index, reset_gpios), \
DT_INST_GPIO_FLAGS(index, reset_gpios)), \
.pixel_format = DT_INST_PROP(index, pixel_format), \
.rotation = DT_INST_PROP(index, rotation), \
.gamset = DT_INST_PROP(index, gamset), \
.frmctr1 = DT_INST_PROP(index, frmctr1), \
.disctrl = DT_INST_PROP(index, disctrl), \
.pwctrl1 = DT_INST_PROP(index, pwctrl1), \
.pwctrl2 = DT_INST_PROP(index, pwctrl2), \
.vmctrl1 = DT_INST_PROP(index, vmctrl1), \
.vmctrl2 = DT_INST_PROP(index, vmctrl2), \
.pgamctrl = DT_INST_PROP(index, pgamctrl), \
.ngamctrl = DT_INST_PROP(index, ngamctrl), \
}; \
\
static struct ili9340_data ili9340_data_##index; \
\
DEVICE_AND_API_INIT(ili9340_##index, DT_INST_LABEL(index), \
ili9340_init, &ili9340_data_##index, \
&ili9340_config_##index, POST_KERNEL, \
CONFIG_APPLICATION_INIT_PRIORITY, &ili9340_api);
DT_INST_FOREACH_STATUS_OKAY(ILI9340_INIT)

View file

@ -1,6 +1,4 @@
/*
* Copyright (c) 2017 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright (c) 2019 Nordic Semiconductor ASA
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
@ -8,18 +6,10 @@
#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_
#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_
#include <sys/util.h>
#include <device.h>
/* Commands/registers. */
#define ILI9340_SLPOUT 0x11
#define ILI9340_GAMSET 0x26
#define ILI9340_DISPOFF 0x28
#define ILI9340_DISPON 0x29
#define ILI9340_CASET 0x2a
#define ILI9340_PASET 0x2b
#define ILI9340_RAMWR 0x2c
#define ILI9340_MADCTL 0x36
#define ILI9340_PIXSET 0x3A
#define ILI9340_FRMCTR1 0xB1
#define ILI9340_DISCTRL 0xB6
#define ILI9340_PWCTRL1 0xC0
@ -40,38 +30,44 @@
#define ILI9340_PGAMCTRL_LEN 15U
#define ILI9340_NGAMCTRL_LEN 15U
/* MADCTL register fields. */
#define ILI9340_MADCTL_MY BIT(7U)
#define ILI9340_MADCTL_MX BIT(6U)
#define ILI9340_MADCTL_MV BIT(5U)
#define ILI9340_MADCTL_ML BIT(4U)
#define ILI9340_MADCTL_BGR BIT(3U)
#define ILI9340_MADCTL_MH BIT(2U)
/* PIXSET register fields. */
#define ILI9340_PIXSET_RGB_18_BIT 0x60
#define ILI9340_PIXSET_RGB_16_BIT 0x50
#define ILI9340_PIXSET_MCU_18_BIT 0x06
#define ILI9340_PIXSET_MCU_16_BIT 0x05
/** Command/data GPIO level for commands. */
#define ILI9340_CMD 1U
/** Command/data GPIO level for data. */
#define ILI9340_DATA 0U
/** Sleep out time (ms), ref. 8.2.12 of ILI9340 manual. */
#define ILI9340_SLEEP_OUT_TIME 120
/** Reset pulse time (ms), ref 15.4 of ILI9340 manual. */
#define ILI9340_RESET_PULSE_TIME 1
/** Reset wait time (ms), ref 15.4 of ILI9340 manual. */
#define ILI9340_RESET_WAIT_TIME 5
/** X resolution (pixels). */
#define ILI9340_X_RES 240U
/** Y resolution (pixels). */
#define ILI9340_Y_RES 320U
/** ILI9340 registers to be initialized. */
struct ili9340_regs {
uint8_t gamset[ILI9340_GAMSET_LEN];
uint8_t frmctr1[ILI9340_FRMCTR1_LEN];
uint8_t disctrl[ILI9340_DISCTRL_LEN];
uint8_t pwctrl1[ILI9340_PWCTRL1_LEN];
uint8_t pwctrl2[ILI9340_PWCTRL2_LEN];
uint8_t vmctrl1[ILI9340_VMCTRL1_LEN];
uint8_t vmctrl2[ILI9340_VMCTRL2_LEN];
uint8_t pgamctrl[ILI9340_PGAMCTRL_LEN];
uint8_t ngamctrl[ILI9340_NGAMCTRL_LEN];
};
/* Initializer macro for ILI9340 registers. */
#define ILI9340_REGS_INIT(n) \
static const struct ili9340_regs ili9xxx_regs_##n = { \
.gamset = DT_PROP(DT_INST(n, ilitek_ili9340), gamset), \
.frmctr1 = DT_PROP(DT_INST(n, ilitek_ili9340), frmctr1), \
.disctrl = DT_PROP(DT_INST(n, ilitek_ili9340), disctrl), \
.pwctrl1 = DT_PROP(DT_INST(n, ilitek_ili9340), pwctrl1), \
.pwctrl2 = DT_PROP(DT_INST(n, ilitek_ili9340), pwctrl2), \
.vmctrl1 = DT_PROP(DT_INST(n, ilitek_ili9340), vmctrl1), \
.vmctrl2 = DT_PROP(DT_INST(n, ilitek_ili9340), vmctrl2), \
.pgamctrl = DT_PROP(DT_INST(n, ilitek_ili9340), pgamctrl), \
.ngamctrl = DT_PROP(DT_INST(n, ilitek_ili9340), ngamctrl), \
}
/**
* @brief Initialize ILI9340 registers with DT values.
*
* @param dev ILI9340 device instance
* @return 0 on success, errno otherwise.
*/
int ili9340_regs_init(const struct device *dev);
#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ */

View file

@ -0,0 +1,491 @@
/*
* Copyright (c) 2017 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright (c) 2019 Nordic Semiconductor ASA
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "display_ili9xxx.h"
#include <dt-bindings/display/ili9xxx.h>
#include <drivers/display.h>
#include <drivers/spi.h>
#include <sys/byteorder.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(display_ili9xxx, CONFIG_DISPLAY_LOG_LEVEL);
struct ili9xxx_data {
const struct device *reset_gpio;
const struct device *command_data_gpio;
const struct device *spi_dev;
struct spi_config spi_config;
struct spi_cs_control cs_ctrl;
uint8_t bytes_per_pixel;
enum display_pixel_format pixel_format;
enum display_orientation orientation;
};
int ili9xxx_transmit(const struct device *dev, uint8_t cmd, const void *tx_data,
size_t tx_len)
{
const struct ili9xxx_config *config =
(struct ili9xxx_config *)dev->config;
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
int r;
struct spi_buf tx_buf;
struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1U };
/* send command */
tx_buf.buf = &cmd;
tx_buf.len = 1U;
gpio_pin_set(data->command_data_gpio, config->cmd_data_pin,
ILI9XXX_CMD);
r = spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
if (r < 0) {
return r;
}
/* send data (if any) */
if (tx_data != NULL) {
tx_buf.buf = (void *)tx_data;
tx_buf.len = tx_len;
gpio_pin_set(data->command_data_gpio, config->cmd_data_pin,
ILI9XXX_DATA);
r = spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
if (r < 0) {
return r;
}
}
return 0;
}
static int ili9xxx_exit_sleep(const struct device *dev)
{
int r;
r = ili9xxx_transmit(dev, ILI9XXX_SLPOUT, NULL, 0);
if (r < 0) {
return r;
}
k_sleep(K_MSEC(ILI9XXX_SLEEP_OUT_TIME));
return 0;
}
static void ili9xxx_hw_reset(const struct device *dev)
{
const struct ili9xxx_config *config =
(struct ili9xxx_config *)dev->config;
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
if (data->reset_gpio == NULL) {
return;
}
gpio_pin_set(data->reset_gpio, config->reset_pin, 1);
k_sleep(K_MSEC(ILI9XXX_RESET_PULSE_TIME));
gpio_pin_set(data->reset_gpio, config->reset_pin, 0);
k_sleep(K_MSEC(ILI9XXX_RESET_WAIT_TIME));
}
static int ili9xxx_set_mem_area(const struct device *dev, const uint16_t x,
const uint16_t y, const uint16_t w,
const uint16_t h)
{
int r;
uint16_t spi_data[2];
spi_data[0] = sys_cpu_to_be16(x);
spi_data[1] = sys_cpu_to_be16(x + w - 1U);
r = ili9xxx_transmit(dev, ILI9XXX_CASET, &spi_data[0], 4U);
if (r < 0) {
return r;
}
spi_data[0] = sys_cpu_to_be16(y);
spi_data[1] = sys_cpu_to_be16(y + h - 1U);
r = ili9xxx_transmit(dev, ILI9XXX_PASET, &spi_data[0], 4U);
if (r < 0) {
return r;
}
return 0;
}
static int ili9xxx_write(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
const void *buf)
{
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
int r;
const uint8_t *write_data_start = (const uint8_t *)buf;
struct spi_buf tx_buf;
struct spi_buf_set tx_bufs;
uint16_t write_cnt;
uint16_t nbr_of_writes;
uint16_t write_h;
__ASSERT(desc->width <= desc->pitch, "Pitch is smaller than width");
__ASSERT((desc->pitch * data->bytes_per_pixel * desc->height) <=
desc->buf_size,
"Input buffer to small");
LOG_DBG("Writing %dx%d (w,h) @ %dx%d (x,y)", desc->width, desc->height,
x, y);
r = ili9xxx_set_mem_area(dev, x, y, desc->width, desc->height);
if (r < 0) {
return r;
}
if (desc->pitch > desc->width) {
write_h = 1U;
nbr_of_writes = desc->height;
} else {
write_h = desc->height;
nbr_of_writes = 1U;
}
r = ili9xxx_transmit(dev, ILI9XXX_RAMWR, write_data_start,
desc->width * data->bytes_per_pixel * write_h);
if (r < 0) {
return r;
}
tx_bufs.buffers = &tx_buf;
tx_bufs.count = 1;
write_data_start += desc->pitch * data->bytes_per_pixel;
for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) {
tx_buf.buf = (void *)write_data_start;
tx_buf.len = desc->width * data->bytes_per_pixel * write_h;
r = spi_write(data->spi_dev, &data->spi_config, &tx_bufs);
if (r < 0) {
return r;
}
write_data_start += desc->pitch * data->bytes_per_pixel;
}
return 0;
}
static int ili9xxx_read(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc, void *buf)
{
LOG_ERR("Reading not supported");
return -ENOTSUP;
}
static void *ili9xxx_get_framebuffer(const struct device *dev)
{
LOG_ERR("Direct framebuffer access not supported");
return NULL;
}
static int ili9xxx_display_blanking_off(const struct device *dev)
{
LOG_DBG("Turning display blanking off");
return ili9xxx_transmit(dev, ILI9XXX_DISPON, NULL, 0);
}
static int ili9xxx_display_blanking_on(const struct device *dev)
{
LOG_DBG("Turning display blanking on");
return ili9xxx_transmit(dev, ILI9XXX_DISPOFF, NULL, 0);
}
static int ili9xxx_set_brightness(const struct device *dev,
const uint8_t brightness)
{
LOG_ERR("Set brightness not implemented");
return -ENOTSUP;
}
static int ili9xxx_set_contrast(const struct device *dev,
const uint8_t contrast)
{
LOG_ERR("Set contrast not supported");
return -ENOTSUP;
}
static int
ili9xxx_set_pixel_format(const struct device *dev,
const enum display_pixel_format pixel_format)
{
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
int r;
uint8_t tx_data;
uint8_t bytes_per_pixel;
if (pixel_format == PIXEL_FORMAT_RGB_565) {
bytes_per_pixel = 2U;
tx_data = ILI9XXX_PIXSET_MCU_16_BIT | ILI9XXX_PIXSET_RGB_16_BIT;
} else if (pixel_format == PIXEL_FORMAT_RGB_888) {
bytes_per_pixel = 3U;
tx_data = ILI9XXX_PIXSET_MCU_18_BIT | ILI9XXX_PIXSET_RGB_18_BIT;
} else {
LOG_ERR("Unsupported pixel format");
return -ENOTSUP;
}
r = ili9xxx_transmit(dev, ILI9XXX_PIXSET, &tx_data, 1U);
if (r < 0) {
return r;
}
data->pixel_format = pixel_format;
data->bytes_per_pixel = bytes_per_pixel;
return 0;
}
static int ili9xxx_set_orientation(const struct device *dev,
const enum display_orientation orientation)
{
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
int r;
uint8_t tx_data = ILI9XXX_MADCTL_BGR;
if (orientation == DISPLAY_ORIENTATION_NORMAL) {
tx_data |= ILI9XXX_MADCTL_MX;
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_90) {
tx_data |= ILI9XXX_MADCTL_MV;
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_180) {
tx_data |= ILI9XXX_MADCTL_MY;
} else if (orientation == DISPLAY_ORIENTATION_ROTATED_270) {
tx_data |= ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MX |
ILI9XXX_MADCTL_MY;
}
r = ili9xxx_transmit(dev, ILI9XXX_MADCTL, &tx_data, 1U);
if (r < 0) {
return r;
}
data->orientation = orientation;
return 0;
}
static void ili9xxx_get_capabilities(const struct device *dev,
struct display_capabilities *capabilities)
{
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
const struct ili9xxx_config *config =
(struct ili9xxx_config *)dev->config;
memset(capabilities, 0, sizeof(struct display_capabilities));
capabilities->supported_pixel_formats =
PIXEL_FORMAT_RGB_565 | PIXEL_FORMAT_RGB_888;
capabilities->current_pixel_format = data->pixel_format;
if (data->orientation == DISPLAY_ORIENTATION_NORMAL ||
data->orientation == DISPLAY_ORIENTATION_ROTATED_180) {
capabilities->x_resolution = config->x_resolution;
capabilities->y_resolution = config->y_resolution;
} else {
capabilities->x_resolution = config->y_resolution;
capabilities->y_resolution = config->x_resolution;
}
capabilities->current_orientation = data->orientation;
}
static int ili9xxx_configure(const struct device *dev)
{
const struct ili9xxx_config *config =
(struct ili9xxx_config *)dev->config;
int r;
enum display_pixel_format pixel_format;
enum display_orientation orientation;
/* pixel format */
if (config->pixel_format == ILI9XXX_PIXEL_FORMAT_RGB565) {
pixel_format = PIXEL_FORMAT_RGB_565;
} else {
pixel_format = PIXEL_FORMAT_RGB_888;
}
r = ili9xxx_set_pixel_format(dev, pixel_format);
if (r < 0) {
return r;
}
/* orientation */
if (config->rotation == 0U) {
orientation = DISPLAY_ORIENTATION_NORMAL;
} else if (config->rotation == 90U) {
orientation = DISPLAY_ORIENTATION_ROTATED_90;
} else if (config->rotation == 180U) {
orientation = DISPLAY_ORIENTATION_ROTATED_180;
} else {
orientation = DISPLAY_ORIENTATION_ROTATED_270;
}
r = ili9xxx_set_orientation(dev, orientation);
if (r < 0) {
return r;
}
r = config->regs_init_fn(dev);
if (r < 0) {
return r;
}
return 0;
}
static int ili9xxx_init(const struct device *dev)
{
const struct ili9xxx_config *config =
(struct ili9xxx_config *)dev->config;
struct ili9xxx_data *data = (struct ili9xxx_data *)dev->data;
int r;
data->spi_dev = device_get_binding(config->spi_name);
if (data->spi_dev == NULL) {
LOG_ERR("Could not get SPI device %s", config->spi_name);
return -ENODEV;
}
data->spi_config.frequency = config->spi_max_freq;
data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8U);
data->spi_config.slave = config->spi_addr;
data->cs_ctrl.gpio_dev = device_get_binding(config->spi_cs_label);
if (data->cs_ctrl.gpio_dev != NULL) {
data->cs_ctrl.gpio_pin = config->spi_cs_pin;
data->cs_ctrl.gpio_dt_flags = config->spi_cs_flags;
data->cs_ctrl.delay = 0U;
data->spi_config.cs = &data->cs_ctrl;
}
data->command_data_gpio = device_get_binding(config->cmd_data_label);
if (data->command_data_gpio == NULL) {
LOG_ERR("Could not get command/data GPIO port %s",
config->cmd_data_label);
return -ENODEV;
}
r = gpio_pin_configure(data->command_data_gpio, config->cmd_data_pin,
GPIO_OUTPUT | config->cmd_data_flags);
if (r < 0) {
LOG_ERR("Could not configure command/data GPIO (%d)", r);
return r;
}
data->reset_gpio = device_get_binding(config->reset_label);
if (data->reset_gpio != NULL) {
r = gpio_pin_configure(data->reset_gpio, config->reset_pin,
GPIO_OUTPUT_INACTIVE |
config->reset_flags);
if (r < 0) {
LOG_ERR("Could not configure reset GPIO (%d)", r);
return r;
}
}
ili9xxx_hw_reset(dev);
r = ili9xxx_configure(dev);
if (r < 0) {
LOG_ERR("Could not configure display (%d)", r);
return r;
}
r = ili9xxx_exit_sleep(dev);
if (r < 0) {
LOG_ERR("Could not exit sleep mode (%d)", r);
return r;
}
return 0;
}
static const struct display_driver_api ili9xxx_api = {
.blanking_on = ili9xxx_display_blanking_on,
.blanking_off = ili9xxx_display_blanking_off,
.write = ili9xxx_write,
.read = ili9xxx_read,
.get_framebuffer = ili9xxx_get_framebuffer,
.set_brightness = ili9xxx_set_brightness,
.set_contrast = ili9xxx_set_contrast,
.get_capabilities = ili9xxx_get_capabilities,
.set_pixel_format = ili9xxx_set_pixel_format,
.set_orientation = ili9xxx_set_orientation,
};
#define INST_DT_ILI9XXX(n, t) DT_INST(n, ilitek_ili##t)
#define ILI9XXX_INIT(n, t) \
ILI##t##_REGS_INIT(n); \
\
static const struct ili9xxx_config ili9xxx_config_##n = { \
.spi_name = DT_BUS_LABEL(INST_DT_ILI9XXX(n, t)), \
.spi_addr = DT_REG_ADDR(INST_DT_ILI9XXX(n, t)), \
.spi_max_freq = UTIL_AND( \
DT_HAS_PROP(INST_DT_ILI9XXX(n, t), spi_max_frequency), \
DT_PROP(INST_DT_ILI9XXX(n, t), spi_max_frequency)), \
.spi_cs_label = UTIL_AND( \
DT_SPI_DEV_HAS_CS_GPIOS(INST_DT_ILI9XXX(n, t)), \
DT_SPI_DEV_CS_GPIOS_LABEL(INST_DT_ILI9XXX(n, t))), \
.spi_cs_pin = UTIL_AND( \
DT_SPI_DEV_HAS_CS_GPIOS(INST_DT_ILI9XXX(n, t)), \
DT_SPI_DEV_CS_GPIOS_PIN(INST_DT_ILI9XXX(n, t))), \
.spi_cs_flags = UTIL_AND( \
DT_SPI_DEV_HAS_CS_GPIOS(INST_DT_ILI9XXX(n, t)), \
DT_SPI_DEV_CS_GPIOS_FLAGS(INST_DT_ILI9XXX(n, t))), \
.cmd_data_label = \
DT_GPIO_LABEL(INST_DT_ILI9XXX(n, t), cmd_data_gpios), \
.cmd_data_pin = \
DT_GPIO_PIN(INST_DT_ILI9XXX(n, t), cmd_data_gpios), \
.cmd_data_flags = \
DT_GPIO_FLAGS(INST_DT_ILI9XXX(n, t), cmd_data_gpios), \
.reset_label = UTIL_AND( \
DT_NODE_HAS_PROP(INST_DT_ILI9XXX(n, t), reset_gpios), \
DT_GPIO_LABEL(INST_DT_ILI9XXX(n, t), reset_gpios)), \
.reset_pin = UTIL_AND( \
DT_NODE_HAS_PROP(INST_DT_ILI9XXX(n, t), reset_gpios), \
DT_GPIO_PIN(INST_DT_ILI9XXX(n, t), reset_gpios)), \
.reset_flags = UTIL_AND( \
DT_NODE_HAS_PROP(INST_DT_ILI9XXX(n, t), reset_gpios), \
DT_GPIO_FLAGS(INST_DT_ILI9XXX(n, t), reset_gpios)), \
.pixel_format = DT_PROP(INST_DT_ILI9XXX(n, t), pixel_format), \
.rotation = DT_PROP(INST_DT_ILI9XXX(n, t), rotation), \
.x_resolution = ILI##t##_X_RES, \
.y_resolution = ILI##t##_Y_RES, \
.regs = &ili9xxx_regs_##n, \
.regs_init_fn = ili##t##_regs_init, \
}; \
\
static struct ili9xxx_data ili9xxx_data_##n; \
\
DEVICE_AND_API_INIT(ili9xxx_##n, DT_LABEL(INST_DT_ILI9XXX(n, t)), \
ili9xxx_init, &ili9xxx_data_##n, \
&ili9xxx_config_##n, POST_KERNEL, \
CONFIG_APPLICATION_INIT_PRIORITY, &ili9xxx_api);
#define DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(t) \
UTIL_LISTIFY(DT_NUM_INST_STATUS_OKAY(ilitek_ili##t), ILI9XXX_INIT, t)
#ifdef CONFIG_ILI9340
#include "display_ili9340.h"
DT_INST_FOREACH_ILI9XXX_STATUS_OKAY(9340);
#endif

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2017 Jan Van Winkel <jan.van_winkel@dxplore.eu>
* Copyright (c) 2019 Nordic Semiconductor ASA
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9XXX_H_
#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9XXX_H_
#include <drivers/gpio.h>
#include <sys/util.h>
/* Commands/registers. */
#define ILI9XXX_SLPOUT 0x11
#define ILI9XXX_GAMSET 0x26
#define ILI9XXX_DISPOFF 0x28
#define ILI9XXX_DISPON 0x29
#define ILI9XXX_CASET 0x2a
#define ILI9XXX_PASET 0x2b
#define ILI9XXX_RAMWR 0x2c
#define ILI9XXX_MADCTL 0x36
#define ILI9XXX_PIXSET 0x3A
/* MADCTL register fields. */
#define ILI9XXX_MADCTL_MY BIT(7U)
#define ILI9XXX_MADCTL_MX BIT(6U)
#define ILI9XXX_MADCTL_MV BIT(5U)
#define ILI9XXX_MADCTL_ML BIT(4U)
#define ILI9XXX_MADCTL_BGR BIT(3U)
#define ILI9XXX_MADCTL_MH BIT(2U)
/* PIXSET register fields. */
#define ILI9XXX_PIXSET_RGB_18_BIT 0x60
#define ILI9XXX_PIXSET_RGB_16_BIT 0x50
#define ILI9XXX_PIXSET_MCU_18_BIT 0x06
#define ILI9XXX_PIXSET_MCU_16_BIT 0x05
/** Command/data GPIO level for commands. */
#define ILI9XXX_CMD 1U
/** Command/data GPIO level for data. */
#define ILI9XXX_DATA 0U
/** Sleep out time (ms), ref. 8.2.12 of ILI9XXX manual. */
#define ILI9XXX_SLEEP_OUT_TIME 120
/** Reset pulse time (ms), ref 15.4 of ILI9XXX manual. */
#define ILI9XXX_RESET_PULSE_TIME 1
/** Reset wait time (ms), ref 15.4 of ILI9XXX manual. */
#define ILI9XXX_RESET_WAIT_TIME 5
struct ili9xxx_config {
const char *spi_name;
uint16_t spi_addr;
uint32_t spi_max_freq;
const char *spi_cs_label;
gpio_pin_t spi_cs_pin;
gpio_dt_flags_t spi_cs_flags;
const char *cmd_data_label;
gpio_pin_t cmd_data_pin;
gpio_dt_flags_t cmd_data_flags;
const char *reset_label;
gpio_pin_t reset_pin;
gpio_dt_flags_t reset_flags;
uint8_t pixel_format;
uint16_t rotation;
uint16_t x_resolution;
uint16_t y_resolution;
const void *regs;
int (*regs_init_fn)(const struct device *dev);
};
int ili9xxx_transmit(const struct device *dev, uint8_t cmd,
const void *tx_data, size_t tx_len);
#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9XXX_H_ */

View file

@ -1,53 +1,14 @@
# Copyright (c) 2018, Jan Van Winkel <jan.van_winkel@dxplore.eu>
# Copyright (c) 2020, Teslabs Engineering S.L.
# SPDX-License-Identifier: Apache-2.0
description: ILI9340 320x240 display controller
compatible: "ilitek,ili9340"
include: spi-device.yaml
include: ilitek,ili9xxx-common.yaml
properties:
reset-gpios:
type: phandle-array
required: false
description: RESET pin.
The RESET pin of ILI9340 is active low.
If connected directly the MCU pin should be configured
as active low.
cmd-data-gpios:
type: phandle-array
required: true
description: D/CX pin.
The D/CX pin of ILI9340 is active low (transmission command byte).
If connected directly the MCU pin should be configured
as active low.
pixel-format:
type: int
default: 0
enum:
- 0 # RGB565
- 1 # RGB888
description:
Display pixel format. Note that when RGB888 pixel format is selected
only 6 color bits are actually used being in practice equivalent to
RGB666.
rotation:
type: int
default: 0
enum:
- 0
- 90
- 180
- 270
description:
Display rotation (CW) in degrees.
gamset:
type: uint8-array
default: [0x01]

View file

@ -0,0 +1,48 @@
# Copyright (c) 2018, Jan Van Winkel <jan.van_winkel@dxplore.eu>
# Copyright (c) 2020, Teslabs Engineering S.L.
# SPDX-License-Identifier: Apache-2.0
description: ILI9XXX display controllers common properties.
include: spi-device.yaml
properties:
reset-gpios:
type: phandle-array
required: false
description: RESET pin.
The RESET pin of ILI9340 is active low.
If connected directly the MCU pin should be configured
as active low.
cmd-data-gpios:
type: phandle-array
required: true
description: D/CX pin.
The D/CX pin of ILI9340 is active low (transmission command byte).
If connected directly the MCU pin should be configured
as active low.
pixel-format:
type: int
default: 0
enum:
- 0 # RGB565
- 1 # RGB888
description:
Display pixel format. Note that when RGB888 pixel format is selected
only 6 color bits are actually used being in practice equivalent to
RGB666.
rotation:
type: int
default: 0
enum:
- 0
- 90
- 180
- 270
description:
Display rotation (CW) in degrees.

View file

@ -1,13 +0,0 @@
/*
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_DISPLAY_ILI9340_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_DISPLAY_ILI9340_H_
/* Pixel formats */
#define ILI9340_PIXEL_FORMAT_RGB565 0U
#define ILI9340_PIXEL_FORMAT_RGB888 1U
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_DISPLAY_ILI9340_H_ */

View file

@ -0,0 +1,13 @@
/*
* Copyright (c) 2020 Teslabs Engineering S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_DISPLAY_ILI9XXX_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_DISPLAY_ILI9XXX_H_
/* Pixel formats */
#define ILI9XXX_PIXEL_FORMAT_RGB565 0U
#define ILI9XXX_PIXEL_FORMAT_RGB888 1U
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_DISPLAY_ILI9XXX_H_ */