drivers: display: Added Sharp memory display drivers.

Added support for sharp memory displays of the series
LS0XX.

Signed-off-by: Rohit Gujarathi <gujju.rohit@gmail.com>
This commit is contained in:
Rohit Gujarathi 2020-08-27 22:56:52 +05:30 committed by Carles Cufí
commit 6910725ad9
5 changed files with 415 additions and 0 deletions

View file

@ -9,6 +9,7 @@ zephyr_sources_ifdef(CONFIG_DUMMY_DISPLAY display_dummy.c)
zephyr_sources_ifdef(CONFIG_FRAMEBUF_DISPLAY display_framebuf.c) zephyr_sources_ifdef(CONFIG_FRAMEBUF_DISPLAY display_framebuf.c)
zephyr_sources_ifdef(CONFIG_ST7789V display_st7789v.c) zephyr_sources_ifdef(CONFIG_ST7789V display_st7789v.c)
zephyr_sources_ifdef(CONFIG_GD7965 gd7965.c) zephyr_sources_ifdef(CONFIG_GD7965 gd7965.c)
zephyr_sources_ifdef(CONFIG_LS0XX ls0xx.c)
if (CONFIG_ILI9XXX) if (CONFIG_ILI9XXX)
zephyr_sources(display_ili9xxx.c) zephyr_sources(display_ili9xxx.c)

View file

@ -24,6 +24,7 @@ source "drivers/display/Kconfig.ssd16xx"
source "drivers/display/Kconfig.st7789v" source "drivers/display/Kconfig.st7789v"
source "drivers/display/Kconfig.gd7965" source "drivers/display/Kconfig.gd7965"
source "drivers/display/Kconfig.dummy" source "drivers/display/Kconfig.dummy"
source "drivers/display/Kconfig.ls0xx"
config FRAMEBUF_DISPLAY config FRAMEBUF_DISPLAY
# Hidden, selected by client drivers. # Hidden, selected by client drivers.

View file

@ -0,0 +1,8 @@
# Copyright (c) 2020 Rohit Gujarathi
# SPDX-License-Identifier: Apache-2.0
config LS0XX
bool "LS0XX memory display controller driver"
depends on SPI
help
Enable driver for sharp memory display series LS0XXX7DXXX

360
drivers/display/ls0xx.c Normal file
View file

@ -0,0 +1,360 @@
/*
* Copyright (c) 2020 Rohit Gujarathi
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT sharp_ls0xx
#include <logging/log.h>
LOG_MODULE_REGISTER(ls0xx, CONFIG_DISPLAY_LOG_LEVEL);
#include <string.h>
#include <device.h>
#include <drivers/display.h>
#include <init.h>
#include <drivers/gpio.h>
#include <drivers/spi.h>
#include <sys/byteorder.h>
/* Supports LS012B7DD01, LS012B7DD06, LS013B7DH03, LS013B7DH05
* LS013B7DH06, LS027B7DH01A, LS032B7DD02, LS044Q7DH01
*/
/* Note:
* -> high/1 means white, low/0 means black
* -> Display expects LSB first
*/
#define LS0XX_PANEL_WIDTH DT_INST_PROP(0, width)
#define LS0XX_PANEL_HEIGHT DT_INST_PROP(0, height)
#define LS0XX_PIXELS_PER_BYTE 8U
/* Adding 2 for the line number and dummy byte
* line_buf format for each row.
* +-------------------+-------------------+----------------+
* | line num (8 bits) | data (WIDTH bits) | dummy (8 bits) |
* +-------------------+-------------------+----------------+
*/
#define LS0XX_BYTES_PER_LINE ((LS0XX_PANEL_WIDTH / LS0XX_PIXELS_PER_BYTE) + 2)
#define LS0XX_BIT_WRITECMD 0x01
#define LS0XX_BIT_VCOM 0x02
#define LS0XX_BIT_CLEAR 0x04
struct ls0xx_data {
#if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
const struct device *disp_dev;
#endif
#if DT_INST_NODE_HAS_PROP(0, extcomin_gpios)
const struct device *extcomin_dev;
#endif
const struct device *spi_dev;
struct spi_cs_control cs_ctrl;
struct spi_config spi_config;
};
#if DT_INST_NODE_HAS_PROP(0, extcomin_gpios)
/* Driver will handle VCOM toggling */
static void ls0xx_vcom_toggle(void *a, void *b, void *c)
{
struct ls0xx_data *driver = (struct ls0xx_data *)a;
while (1) {
gpio_pin_toggle(driver->extcomin_dev,
DT_INST_GPIO_PIN(0, extcomin_gpios));
k_usleep(3);
gpio_pin_toggle(driver->extcomin_dev,
DT_INST_GPIO_PIN(0, extcomin_gpios));
k_msleep(1000 / DT_INST_PROP(0, extcomin_frequency));
}
}
K_THREAD_STACK_DEFINE(vcom_toggle_stack, 256);
struct k_thread vcom_toggle_thread;
#endif
static int ls0xx_blanking_off(const struct device *dev)
{
#if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
struct ls0xx_data *driver = dev->data;
return gpio_pin_set(driver->disp_dev,
DT_INST_GPIO_PIN(0, disp_en_gpios), 1);
#else
LOG_WRN("Unsupported");
return -ENOTSUP;
#endif
}
static int ls0xx_blanking_on(const struct device *dev)
{
#if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
struct ls0xx_data *driver = dev->data;
return gpio_pin_set(driver->disp_dev,
DT_INST_GPIO_PIN(0, disp_en_gpios), 0);
#else
LOG_WRN("Unsupported");
return -ENOTSUP;
#endif
}
static int ls0xx_cmd(const struct device *dev, uint8_t *buf, uint8_t len)
{
struct ls0xx_data *driver = dev->data;
struct spi_buf cmd_buf = { .buf = buf, .len = len };
struct spi_buf_set buf_set = { .buffers = &cmd_buf, .count = 1 };
return spi_write(driver->spi_dev, &driver->spi_config, &buf_set);
}
static int ls0xx_clear(const struct device *dev)
{
struct ls0xx_data *driver = dev->data;
uint8_t clear_cmd[2] = { LS0XX_BIT_CLEAR, 0 };
int err;
err = ls0xx_cmd(dev, clear_cmd, sizeof(clear_cmd));
spi_release(driver->spi_dev, &driver->spi_config);
return err;
}
static int ls0xx_update_display(const struct device *dev,
uint16_t start_line,
uint16_t num_lines,
const uint8_t *data)
{
struct ls0xx_data *driver = dev->data;
uint8_t write_cmd[1] = { LS0XX_BIT_WRITECMD };
uint8_t ln = start_line;
uint8_t dummy = 27;
struct spi_buf line_buf[3] = {
{
.len = sizeof(ln),
.buf = &ln,
},
{
.len = LS0XX_BYTES_PER_LINE - 2,
},
{
.len = sizeof(dummy),
.buf = &dummy,
},
};
struct spi_buf_set line_set = {
.buffers = line_buf,
.count = ARRAY_SIZE(line_buf),
};
int err;
LOG_DBG("Lines %d to %d", start_line, start_line + num_lines - 1);
err = ls0xx_cmd(dev, write_cmd, sizeof(write_cmd));
/* Send each line to the screen including
* the line number and dummy bits
*/
for (; ln <= start_line + num_lines - 1; ln++) {
line_buf[1].buf = (uint8_t *)data;
err |= spi_write(driver->spi_dev, &driver->spi_config,
&line_set);
data += LS0XX_PANEL_WIDTH / LS0XX_PIXELS_PER_BYTE;
}
/* Send another trailing 8 bits for the last line
* These can be any bits, it does not matter
* just reusing the write_cmd buffer
*/
err |= ls0xx_cmd(dev, write_cmd, sizeof(write_cmd));
spi_release(driver->spi_dev, &driver->spi_config);
return err;
}
/* Buffer width should be equal to display width */
static int ls0xx_write(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
const void *buf)
{
LOG_DBG("X: %d, Y: %d, W: %d, H: %d", x, y, desc->width, desc->height);
if (buf == NULL) {
LOG_WRN("Display buffer is not available");
return -EINVAL;
}
if (desc->width != LS0XX_PANEL_WIDTH) {
LOG_ERR("Width not a multiple of %d", LS0XX_PANEL_WIDTH);
return -EINVAL;
}
if (desc->pitch != desc->width) {
LOG_ERR("Unsupported mode");
return -ENOTSUP;
}
if ((y + desc->height) > LS0XX_PANEL_HEIGHT) {
LOG_ERR("Buffer out of bounds (height)");
return -EINVAL;
}
if (x != 0) {
LOG_ERR("X-coordinate has to be 0");
return -EINVAL;
}
/* Adding 1 since line numbering on the display starts with 1 */
return ls0xx_update_display(dev, y + 1, desc->height, buf);
}
static int ls0xx_read(const struct device *dev, const uint16_t x,
const uint16_t y,
const struct display_buffer_descriptor *desc,
void *buf)
{
LOG_ERR("not supported");
return -ENOTSUP;
}
static void *ls0xx_get_framebuffer(const struct device *dev)
{
LOG_ERR("not supported");
return NULL;
}
static int ls0xx_set_brightness(const struct device *dev,
const uint8_t brightness)
{
LOG_WRN("not supported");
return -ENOTSUP;
}
static int ls0xx_set_contrast(const struct device *dev, uint8_t contrast)
{
LOG_WRN("not supported");
return -ENOTSUP;
}
static void ls0xx_get_capabilities(const struct device *dev,
struct display_capabilities *caps)
{
memset(caps, 0, sizeof(struct display_capabilities));
caps->x_resolution = LS0XX_PANEL_WIDTH;
caps->y_resolution = LS0XX_PANEL_HEIGHT;
caps->supported_pixel_formats = PIXEL_FORMAT_MONO01;
caps->current_pixel_format = PIXEL_FORMAT_MONO01;
caps->screen_info = SCREEN_INFO_X_ALIGNMENT_WIDTH;
}
static int ls0xx_set_orientation(const struct device *dev,
const enum display_orientation orientation)
{
LOG_ERR("Unsupported");
return -ENOTSUP;
}
static int ls0xx_set_pixel_format(const struct device *dev,
const enum display_pixel_format pf)
{
if (pf == PIXEL_FORMAT_MONO01) {
return 0;
}
LOG_ERR("not supported");
return -ENOTSUP;
}
static int ls0xx_init(const struct device *dev)
{
struct ls0xx_data *driver = dev->data;
driver->spi_dev = device_get_binding(DT_INST_BUS_LABEL(0));
if (driver->spi_dev == NULL) {
LOG_ERR("Could not get SPI device for LS0XX");
return -EIO;
}
driver->spi_config.frequency = DT_INST_PROP(0, spi_max_frequency);
driver->spi_config.operation = SPI_OP_MODE_MASTER |
SPI_WORD_SET(8) |
SPI_TRANSFER_LSB |
SPI_CS_ACTIVE_HIGH |
SPI_HOLD_ON_CS |
SPI_LOCK_ON;
driver->spi_config.slave = 0;
#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0)
driver->cs_ctrl.gpio_dev = device_get_binding(
DT_INST_SPI_DEV_CS_GPIOS_LABEL(0));
if (driver->cs_ctrl.gpio_dev == NULL) {
LOG_ERR("Could not get SPI device for LS0XX");
return -EIO;
}
driver->cs_ctrl.gpio_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(0);
driver->cs_ctrl.gpio_dt_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0);
driver->cs_ctrl.delay = 0U;
driver->spi_config.cs = &driver->cs_ctrl;
#endif
#if DT_INST_NODE_HAS_PROP(0, disp_en_gpios)
driver->disp_dev = device_get_binding(
DT_INST_GPIO_LABEL(0, disp_en_gpios));
if (driver->disp_dev == NULL) {
LOG_ERR("Could not get DISP pin port for LS0XX");
return -EIO;
}
LOG_INF("Configuring DISP pin to OUTPUT_HIGH");
gpio_pin_configure(driver->disp_dev,
DT_INST_GPIO_PIN(0, disp_en_gpios),
GPIO_OUTPUT_HIGH);
#endif
#if DT_INST_NODE_HAS_PROP(0, extcomin_gpios)
driver->extcomin_dev = device_get_binding(
DT_INST_GPIO_LABEL(0, extcomin_gpios));
if (driver->extcomin_dev == NULL) {
LOG_ERR("Could not get EXTCOMIN pin port for LS0XX");
return -EIO;
}
LOG_INF("Configuring EXTCOMIN pin");
gpio_pin_configure(driver->extcomin_dev,
DT_INST_GPIO_PIN(0, extcomin_gpios),
GPIO_OUTPUT_LOW);
/* Start thread for toggling VCOM */
k_tid_t vcom_toggle_tid = k_thread_create(&vcom_toggle_thread,
vcom_toggle_stack,
K_THREAD_STACK_SIZEOF(vcom_toggle_stack),
ls0xx_vcom_toggle,
driver, NULL, NULL,
3, 0, K_NO_WAIT);
k_thread_name_set(vcom_toggle_tid, "ls0xx_vcom");
#endif /* DT_INST_NODE_HAS_PROP(0, extcomin_gpios) */
/* Clear display else it shows random data */
return ls0xx_clear(dev);
}
static struct ls0xx_data ls0xx_driver;
static struct display_driver_api ls0xx_driver_api = {
.blanking_on = ls0xx_blanking_on,
.blanking_off = ls0xx_blanking_off,
.write = ls0xx_write,
.read = ls0xx_read,
.get_framebuffer = ls0xx_get_framebuffer,
.set_brightness = ls0xx_set_brightness,
.set_contrast = ls0xx_set_contrast,
.get_capabilities = ls0xx_get_capabilities,
.set_pixel_format = ls0xx_set_pixel_format,
.set_orientation = ls0xx_set_orientation,
};
DEVICE_DT_INST_DEFINE(0, ls0xx_init, device_pm_control_nop,
&ls0xx_driver, NULL,
POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY,
&ls0xx_driver_api);

View file

@ -0,0 +1,45 @@
# Copyright (c) 2020, Rohit Gujarathi
# SPDX-License-Identifier: Apache-2.0
description: Sharp memory display controller
compatible: "sharp,ls0xx"
include: spi-device.yaml
properties:
height:
type: int
required: true
description: Height in pixel of the panel driven by the controller
width:
type: int
required: true
description: Width in pixel of the panel driven by the controller
extcomin-gpios:
type: phandle-array
required: false
description: EXTCOMIN pin
The EXTCOMIN pin is where a square pulse for toggling VCOM will
be given
extcomin-frequency:
type: int
required: false
description: EXTCOMIN pin toggle frequency
The frequency with which the EXTCOMIN pin should be toggled. See
datasheet of particular display. Higher frequency gives better
contrast while low frequency saves power.
disp-en-gpios:
type: phandle-array
required: false
description: DISPLAY pin
The DISPLAY pin controls if the LCD displays memory contents or
white screen. If defined, the pin will be set high during driver
initialization. display blanking apis can be used to control it.