drivers: display: Update ST7567 to use MIPI-DBI

Allows ST7567 to use dbi controller

Signed-off-by: Camille BAUD <mail@massdriver.space>
This commit is contained in:
Camille BAUD
2025-09-12 22:21:10 +02:00
committed by Chris Friedt
parent 9b26e967ca
commit 77c078b7ee
4 changed files with 152 additions and 92 deletions

View File

@@ -6,7 +6,7 @@ menuconfig ST7567
default y
depends on DT_HAS_SITRONIX_ST7567_ENABLED
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_SITRONIX_ST7567),i2c)
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_SITRONIX_ST7567),spi)
select MIPI_DBI if $(dt_compat_on_bus,$(DT_COMPAT_SITRONIX_ST7567),mipi-dbi)
help
Enable driver for ST7567 display controller.

View File

@@ -11,29 +11,36 @@ LOG_MODULE_REGISTER(display_st7567, CONFIG_DISPLAY_LOG_LEVEL);
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/mipi_dbi.h>
#include <zephyr/kernel.h>
#include "display_st7567_regs.h"
struct st7567_dbi {
const struct device *mipi_dev;
const struct mipi_dbi_config dbi_config;
};
union st7567_bus {
struct i2c_dt_spec i2c;
struct spi_dt_spec spi;
struct st7567_dbi dbi;
};
typedef bool (*st7567_bus_ready_fn)(const struct device *dev);
typedef int (*st7567_write_bus_fn)(const struct device *dev, uint8_t *buf, size_t len,
bool command);
typedef int (*st7567_write_cmd_bus_fn)(const struct device *dev, const uint8_t *buf, size_t len);
typedef int (*st7567_write_pixels_bus_fn)(const struct device *dev, const uint8_t *buf, size_t len);
typedef void (*st7567_release_bus_fn)(const struct device *dev);
typedef int (*st7567_reset_fn)(const struct device *dev);
typedef const char *(*st7567_bus_name_fn)(const struct device *dev);
struct st7567_config {
union st7567_bus bus;
struct gpio_dt_spec data_cmd;
struct gpio_dt_spec reset;
st7567_bus_ready_fn bus_ready;
st7567_write_bus_fn write_bus;
st7567_write_cmd_bus_fn write_cmd_bus;
st7567_write_pixels_bus_fn write_pixels_bus;
st7567_release_bus_fn release_bus;
st7567_reset_fn reset;
st7567_bus_name_fn bus_name;
uint16_t height;
uint16_t width;
@@ -59,13 +66,18 @@ static bool st7567_bus_ready_i2c(const struct device *dev)
return i2c_is_ready_dt(&config->bus.i2c);
}
static int st7567_write_bus_i2c(const struct device *dev, uint8_t *buf, size_t len, bool command)
static int st7567_write_cmd_bus_i2c(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct st7567_config *config = dev->config;
return i2c_burst_write_dt(
&config->bus.i2c,
command ? ST7567_CONTROL_ALL_BYTES_CMD : ST7567_CONTROL_ALL_BYTES_DATA, buf, len);
return i2c_burst_write_dt(&config->bus.i2c, ST7567_CONTROL_ALL_BYTES_CMD, buf, len);
}
static int st7567_write_pixels_bus_i2c(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct st7567_config *config = dev->config;
return i2c_burst_write_dt(&config->bus.i2c, ST7567_CONTROL_ALL_BYTES_DATA, buf, len);
}
static const char *st7567_bus_name_i2c(const struct device *dev)
@@ -75,41 +87,87 @@ static const char *st7567_bus_name_i2c(const struct device *dev)
return config->bus.i2c.bus->name;
}
static int st7567_reset_i2c(const struct device *dev)
{
/* do nothing */
return 0;
}
static void st7567_release_bus_i2c(const struct device *dev)
{
/* do nothing */
}
#endif
#if DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(sitronix_st7567, spi)
#if DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(sitronix_st7567, mipi_dbi)
static bool st7567_bus_ready_spi(const struct device *dev)
static bool st7567_bus_ready_dbi(const struct device *dev)
{
const struct st7567_config *config = dev->config;
if (gpio_pin_configure_dt(&config->data_cmd, GPIO_OUTPUT_INACTIVE) < 0) {
return false;
}
return spi_is_ready_dt(&config->bus.spi);
return device_is_ready(config->bus.dbi.mipi_dev);
}
static int st7567_write_bus_spi(const struct device *dev, uint8_t *buf, size_t len, bool command)
static int st7567_write_cmd_bus_dbi(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct st7567_config *config = dev->config;
int ret;
gpio_pin_set_dt(&config->data_cmd, command ? 0 : 1);
struct spi_buf tx_buf = {.buf = buf, .len = len};
struct spi_buf_set tx_bufs = {.buffers = &tx_buf, .count = 1};
ret = spi_write_dt(&config->bus.spi, &tx_bufs);
for (size_t i = 0; i < len; i++) {
ret = mipi_dbi_command_write(config->bus.dbi.mipi_dev, &config->bus.dbi.dbi_config,
buf[i], NULL, 0);
if (ret) {
return ret;
}
}
mipi_dbi_release(config->bus.dbi.mipi_dev, &config->bus.dbi.dbi_config);
return ret;
}
static const char *st7567_bus_name_spi(const struct device *dev)
static int st7567_write_pixels_bus_dbi(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct st7567_config *config = dev->config;
struct display_buffer_descriptor mipi_desc;
int ret;
mipi_desc.height = 8;
mipi_desc.width = len;
mipi_desc.pitch = len;
mipi_desc.buf_size = len;
ret = mipi_dbi_write_display(config->bus.dbi.mipi_dev, &config->bus.dbi.dbi_config,
buf, &mipi_desc, PIXEL_FORMAT_MONO01);
return ret;
}
static const char *st7567_bus_name_dbi(const struct device *dev)
{
const struct st7567_config *config = dev->config;
return config->bus.spi.bus->name;
return config->bus.dbi.mipi_dev->name;
}
static int st7567_reset_dbi(const struct device *dev)
{
const struct st7567_config *config = dev->config;
int err;
err = mipi_dbi_reset(config->bus.dbi.mipi_dev, ST7567_RESET_DELAY);
if (err < 0) {
LOG_ERR("Failed to reset device!");
}
return err;
}
static void st7567_release_bus_dbi(const struct device *dev)
{
const struct st7567_config *config = dev->config;
mipi_dbi_release(config->bus.dbi.mipi_dev, &config->bus.dbi.dbi_config);
}
#endif
@@ -121,11 +179,32 @@ static inline bool st7567_bus_ready(const struct device *dev)
return config->bus_ready(dev);
}
static inline int st7567_write_bus(const struct device *dev, uint8_t *buf, size_t len, bool command)
static inline int st7567_write_cmd_bus(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct st7567_config *config = dev->config;
return config->write_bus(dev, buf, len, command);
return config->write_cmd_bus(dev, buf, len);
}
static inline int st7567_write_pixels_bus(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct st7567_config *config = dev->config;
return config->write_pixels_bus(dev, buf, len);
}
static inline void release_bus(const struct device *dev)
{
const struct st7567_config *config = dev->config;
config->release_bus(dev);
}
static inline int st7567_hw_reset(const struct device *dev)
{
const struct st7567_config *config = dev->config;
return config->reset(dev);
}
static inline int st7567_set_panel_orientation(const struct device *dev)
@@ -136,7 +215,7 @@ static inline int st7567_set_panel_orientation(const struct device *dev)
(config->com_invdir ? ST7567_SET_COM_OUTPUT_SCAN_FLIPPED
: ST7567_SET_COM_OUTPUT_SCAN_NORMAL)};
return st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
return st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
}
static inline int st7567_set_hardware_config(const struct device *dev)
@@ -146,38 +225,38 @@ static inline int st7567_set_hardware_config(const struct device *dev)
uint8_t cmd_buf[1];
cmd_buf[0] = ST7567_SET_BIAS | (config->bias ? 1 : 0);
ret = st7567_write_bus(dev, cmd_buf, 1, true);
ret = st7567_write_cmd_bus(dev, cmd_buf, 1);
if (ret < 0) {
return ret;
}
cmd_buf[0] = ST7567_POWER_CONTROL | ST7567_POWER_CONTROL_VB;
ret = st7567_write_bus(dev, cmd_buf, 1, true);
ret = st7567_write_cmd_bus(dev, cmd_buf, 1);
if (ret < 0) {
return ret;
}
cmd_buf[0] = ST7567_POWER_CONTROL | ST7567_POWER_CONTROL_VB | ST7567_POWER_CONTROL_VR;
ret = st7567_write_bus(dev, cmd_buf, 1, true);
ret = st7567_write_cmd_bus(dev, cmd_buf, 1);
if (ret < 0) {
return ret;
}
cmd_buf[0] = ST7567_POWER_CONTROL | ST7567_POWER_CONTROL_VB | ST7567_POWER_CONTROL_VR |
ST7567_POWER_CONTROL_VF;
ret = st7567_write_bus(dev, cmd_buf, 1, true);
ret = st7567_write_cmd_bus(dev, cmd_buf, 1);
if (ret < 0) {
return ret;
}
cmd_buf[0] = ST7567_SET_REGULATION_RATIO | (config->regulation_ratio & 0x7);
ret = st7567_write_bus(dev, cmd_buf, 1, true);
ret = st7567_write_cmd_bus(dev, cmd_buf, 1);
if (ret < 0) {
return ret;
}
cmd_buf[0] = ST7567_LINE_SCROLL | (config->line_offset & 0x3F);
ret = st7567_write_bus(dev, cmd_buf, 1, true);
ret = st7567_write_cmd_bus(dev, cmd_buf, 1);
if (ret < 0) {
return ret;
}
@@ -192,7 +271,7 @@ static int st7567_resume(const struct device *dev)
ST7567_DISPLAY_ON,
};
return st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
return st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
}
static int st7567_suspend(const struct device *dev)
@@ -202,7 +281,7 @@ static int st7567_suspend(const struct device *dev)
ST7567_DISPLAY_ALL_PIXEL_ON,
};
return st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
return st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
}
static int st7567_write_default(const struct device *dev, const uint16_t x, const uint16_t y,
@@ -216,16 +295,19 @@ static int st7567_write_default(const struct device *dev, const uint16_t x, cons
cmd_buf[0] = ST7567_COLUMN_LSB | (x & 0xF);
cmd_buf[1] = ST7567_COLUMN_MSB | ((x >> 4) & 0xF);
cmd_buf[2] = ST7567_PAGE | ((y >> 3) + i);
ret = st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
ret = st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
if (ret < 0) {
return ret;
}
ret = st7567_write_bus(dev, ((uint8_t *)buf + i * desc->pitch), desc->pitch, false);
ret = st7567_write_pixels_bus(dev, ((const uint8_t *)buf + i * desc->pitch),
desc->pitch);
if (ret < 0) {
return ret;
}
}
release_bus(dev);
return ret;
}
@@ -268,7 +350,7 @@ static int st7567_set_contrast(const struct device *dev, const uint8_t contrast)
contrast,
};
return st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
return st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
}
static void st7567_get_capabilities(const struct device *dev, struct display_capabilities *caps)
@@ -304,7 +386,7 @@ static int st7567_set_pixel_format(const struct device *dev, const enum display_
return -ENOTSUP;
}
ret = st7567_write_bus(dev, &cmd, 1, true);
ret = st7567_write_cmd_bus(dev, &cmd, 1);
if (ret) {
LOG_WRN("Couldn't set inversion");
return ret;
@@ -318,21 +400,18 @@ static int st7567_set_pixel_format(const struct device *dev, const enum display_
static int st7567_reset(const struct device *dev)
{
const struct st7567_config *config = dev->config;
int ret;
uint8_t cmd_buf[] = {
ST7567_DISPLAY_OFF,
(config->inversion_on ? ST7567_SET_REVERSE_DISPLAY : ST7567_SET_NORMAL_DISPLAY),
};
/* Reset if pin connected */
if (config->reset.port) {
k_sleep(K_MSEC(ST7567_RESET_DELAY));
gpio_pin_set_dt(&config->reset, 1);
k_sleep(K_MSEC(ST7567_RESET_DELAY));
gpio_pin_set_dt(&config->reset, 0);
k_sleep(K_MSEC(ST7567_RESET_DELAY));
ret = st7567_hw_reset(dev);
if (ret < 0) {
return ret;
}
return st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
return st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
}
static int st7567_clear(const struct device *dev)
@@ -352,12 +431,12 @@ static int st7567_clear(const struct device *dev)
cmd_buf[0] = ST7567_COLUMN_LSB | (x & 0xF);
cmd_buf[1] = ST7567_COLUMN_MSB | ((x >> 4) & 0xF);
cmd_buf[2] = ST7567_PAGE | (y >> 3);
ret = st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
ret = st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
if (ret < 0) {
LOG_ERR("Error clearing display");
return ret;
}
ret = st7567_write_bus(dev, (uint8_t *)&buf, 1, false);
ret = st7567_write_pixels_bus(dev, (uint8_t *)&buf, 1);
if (ret < 0) {
LOG_ERR("Error clearing display");
return ret;
@@ -398,7 +477,7 @@ static int st7567_init_device(const struct device *dev)
}
/* Set inversion */
ret = st7567_write_bus(dev, cmd_buf, sizeof(cmd_buf), true);
ret = st7567_write_cmd_bus(dev, cmd_buf, sizeof(cmd_buf));
if (ret < 0) {
return ret;
}
@@ -423,25 +502,12 @@ static int st7567_init_device(const struct device *dev)
static int st7567_init(const struct device *dev)
{
const struct st7567_config *config = dev->config;
int ret;
if (!st7567_bus_ready(dev)) {
LOG_ERR("Bus device %s not ready!", config->bus_name(dev));
return -EINVAL;
}
if (config->reset.port) {
ret = gpio_pin_configure_dt(&config->reset, GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Couldn't configure reset pin");
return ret;
}
if (!gpio_is_ready_dt(&config->reset)) {
LOG_ERR("Reset GPIO device not ready");
return -ENODEV;
}
}
if (st7567_init_device(dev)) {
LOG_ERR("Failed to initialize device!");
return -EIO;
@@ -460,20 +526,28 @@ static DEVICE_API(display, st7567_driver_api) = {
.set_pixel_format = st7567_set_pixel_format,
};
#define ST7567_CONFIG_SPI(node_id) \
.bus = {.spi = SPI_DT_SPEC_GET( \
node_id, SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_WORD_SET(8))}, \
.bus_ready = st7567_bus_ready_spi, .write_bus = st7567_write_bus_spi, \
.bus_name = st7567_bus_name_spi, .data_cmd = GPIO_DT_SPEC_GET(node_id, data_cmd_gpios),
#define ST7567_WORD_SIZE(inst) \
((DT_STRING_UPPER_TOKEN(inst, mipi_mode) == MIPI_DBI_MODE_SPI_4WIRE) ? SPI_WORD_SET(8) \
: SPI_WORD_SET(9))
#define ST7567_CONFIG_DBI(node_id) \
.bus = {.dbi.dbi_config = MIPI_DBI_CONFIG_DT( \
node_id, ST7567_WORD_SIZE(node_id) | SPI_OP_MODE_MASTER, 0), \
.dbi.mipi_dev = DEVICE_DT_GET(DT_PARENT(node_id)),}, \
.bus_ready = st7567_bus_ready_dbi, \
.write_cmd_bus = st7567_write_cmd_bus_dbi, .write_pixels_bus = st7567_write_pixels_bus_dbi,\
.bus_name = st7567_bus_name_dbi, .release_bus = st7567_release_bus_dbi, \
.reset = st7567_reset_dbi,
#define ST7567_CONFIG_I2C(node_id) \
.bus = {.i2c = I2C_DT_SPEC_GET(node_id)}, .bus_ready = st7567_bus_ready_i2c, \
.write_bus = st7567_write_bus_i2c, .bus_name = st7567_bus_name_i2c, .data_cmd = {0},
.write_cmd_bus = st7567_write_cmd_bus_i2c, .write_pixels_bus = st7567_write_pixels_bus_i2c,\
.bus_name = st7567_bus_name_i2c, .release_bus = st7567_release_bus_i2c, \
.reset = st7567_reset_i2c,
#define ST7567_DEFINE(node_id) \
static struct st7567_data data##node_id; \
static const struct st7567_config config##node_id = { \
.reset = GPIO_DT_SPEC_GET_OR(node_id, reset_gpios, {0}), \
.height = DT_PROP(node_id, height), \
.width = DT_PROP(node_id, width), \
.column_offset = DT_PROP(node_id, column_offset), \
@@ -483,7 +557,7 @@ static DEVICE_API(display, st7567_driver_api) = {
.inversion_on = DT_PROP(node_id, inversion_on), \
.bias = DT_PROP(node_id, bias), \
.regulation_ratio = DT_PROP(node_id, regulation_ratio), \
COND_CODE_1(DT_ON_BUS(node_id, spi), (ST7567_CONFIG_SPI(node_id)), \
COND_CODE_1(DT_ON_BUS(node_id, mipi_dbi), (ST7567_CONFIG_DBI(node_id)), \
(ST7567_CONFIG_I2C(node_id))) }; \
\
DEVICE_DT_DEFINE(node_id, st7567_init, NULL, &data##node_id, &config##node_id, \

View File

@@ -34,11 +34,3 @@ properties:
bias:
type: boolean
description: Bias setting (0 for 1/9, or 1 for 1/7)
reset-gpios:
type: phandle-array
description: RESET pin.
The RESET pin of ST7567 is active low.
If connected directly, the MCU pin should be configured
as active low.

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2025 MASSDRIVER EI (massdriver.space)
# SPDX-License-Identifier: Apache-2.0
title: Sitronix ST7567 dot-matrix LCD controller on SPI bus
title: Sitronix ST7567 dot-matrix LCD controller on MIPI-DBI bus
description: |
The Sitronix ST7567 is a monochrome Dot-Matrix LCD controller
@@ -9,10 +9,4 @@ description: |
compatible: "sitronix,st7567"
include: ["sitronix,st7567-common.yaml", "spi-device.yaml"]
properties:
data-cmd-gpios:
type: phandle-array
required: true
description: D/C# pin.
include: ["sitronix,st7567-common.yaml", "mipi-dbi-spi-device.yaml"]