diff --git a/drivers/display/Kconfig.st7567 b/drivers/display/Kconfig.st7567 index 1a8b5d7e559ef..570d18d306471 100644 --- a/drivers/display/Kconfig.st7567 +++ b/drivers/display/Kconfig.st7567 @@ -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. diff --git a/drivers/display/display_st7567.c b/drivers/display/display_st7567.c index 5f4b7070dbaf4..b391f58212dc2 100644 --- a/drivers/display/display_st7567.c +++ b/drivers/display/display_st7567.c @@ -11,29 +11,36 @@ LOG_MODULE_REGISTER(display_st7567, CONFIG_DISPLAY_LOG_LEVEL); #include #include #include -#include #include -#include +#include #include #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, 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, - 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_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_dbi(const struct device *dev) +{ + const struct st7567_config *config = dev->config; + + return device_is_ready(config->bus.dbi.mipi_dev); +} -static bool st7567_bus_ready_spi(const struct device *dev) +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; - if (gpio_pin_configure_dt(&config->data_cmd, GPIO_OUTPUT_INACTIVE) < 0) { - return false; + 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 spi_is_ready_dt(&config->bus.spi); + return ret; } -static int st7567_write_bus_spi(const struct device *dev, uint8_t *buf, size_t len, bool command) +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; - 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}; + mipi_desc.height = 8; + mipi_desc.width = len; + mipi_desc.pitch = len; + mipi_desc.buf_size = len; - ret = spi_write_dt(&config->bus.spi, &tx_bufs); + 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_spi(const struct device *dev) +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_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; - return config->write_bus(dev, buf, len, command); + 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, \ diff --git a/dts/bindings/display/sitronix,st7567-common.yaml b/dts/bindings/display/sitronix,st7567-common.yaml index 1152a781759b5..e22c61e1d4440 100644 --- a/dts/bindings/display/sitronix,st7567-common.yaml +++ b/dts/bindings/display/sitronix,st7567-common.yaml @@ -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. diff --git a/dts/bindings/display/sitronix,st7567-spi.yaml b/dts/bindings/display/sitronix,st7567-mipi.yaml similarity index 52% rename from dts/bindings/display/sitronix,st7567-spi.yaml rename to dts/bindings/display/sitronix,st7567-mipi.yaml index 6a5d75943d7e3..44e1f38d5e683 100644 --- a/dts/bindings/display/sitronix,st7567-spi.yaml +++ b/dts/bindings/display/sitronix,st7567-mipi.yaml @@ -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"] diff --git a/tests/drivers/build_all/display/app.overlay b/tests/drivers/build_all/display/app.overlay index 70a16e2d195c4..99aba3132a20e 100644 --- a/tests/drivers/build_all/display/app.overlay +++ b/tests/drivers/build_all/display/app.overlay @@ -359,6 +359,18 @@ panel-settings = <0x29>; mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE"; }; + + test_mipi_dbi_st7567: st7567@20 { + compatible = "sitronix,st7567"; + mipi-max-frequency = <1000000>; + reg = <20>; + width = <128>; + height = <64>; + column-offset = <0>; + line-offset = <0>; + regulation-ratio = <7>; + mipi-mode = "MIPI_DBI_MODE_SPI_4WIRE"; + }; }; test_mipi_dbi_xfr_16bit_write_only {