drivers: auxdisplay: Add Hitachi HD44780 driver
Adds an auxiliary display driver for Hitachi HD44780-based (and
compatible) LCD displays.
Signed-off-by: Jamie McCrae <spam@helper3000.net>
diff --git a/drivers/auxdisplay/CMakeLists.txt b/drivers/auxdisplay/CMakeLists.txt
index 2cb1c92..1cc566f 100644
--- a/drivers/auxdisplay/CMakeLists.txt
+++ b/drivers/auxdisplay/CMakeLists.txt
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
+zephyr_library_sources_ifdef(CONFIG_AUXDISPLAY_HD44780 auxdisplay_hd44780.c)
zephyr_library_sources_ifdef(CONFIG_AUXDISPLAY_ITRON auxdisplay_itron.c)
zephyr_library_sources_ifdef(CONFIG_AUXDISPLAY_JHD1313 auxdisplay_jhd1313.c)
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index 94c3cff..4b3c21c 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -20,6 +20,7 @@
module-str = auxdisplay
source "subsys/logging/Kconfig.template.log_config"
+source "drivers/auxdisplay/Kconfig.hd44780"
source "drivers/auxdisplay/Kconfig.itron"
source "drivers/auxdisplay/Kconfig.jhd1313"
diff --git a/drivers/auxdisplay/Kconfig.hd44780 b/drivers/auxdisplay/Kconfig.hd44780
new file mode 100644
index 0000000..5aadae2
--- /dev/null
+++ b/drivers/auxdisplay/Kconfig.hd44780
@@ -0,0 +1,9 @@
+# Copyright (c) 2023 Jamie McCrae
+# SPDX-License-Identifier: Apache-2.0
+
+config AUXDISPLAY_HD44780
+ bool "Hitachi HD44780 LCD driver"
+ default y
+ depends on DT_HAS_HIT_HD44780_ENABLED
+ help
+ Enable driver for Hitachi HD44780 and compatible LCDs.
diff --git a/drivers/auxdisplay/auxdisplay_hd44780.c b/drivers/auxdisplay/auxdisplay_hd44780.c
new file mode 100644
index 0000000..1d0441a
--- /dev/null
+++ b/drivers/auxdisplay/auxdisplay_hd44780.c
@@ -0,0 +1,594 @@
+/*
+ * Copyright (c) 2023 Jamie McCrae
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT hit_hd44780
+
+#include <string.h>
+#include <zephyr/device.h>
+#include <zephyr/devicetree.h>
+#include <zephyr/drivers/auxdisplay.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/pm/device.h>
+#include <zephyr/sys/byteorder.h>
+#include <zephyr/logging/log.h>
+
+LOG_MODULE_REGISTER(auxdisplay_hd44780, CONFIG_AUXDISPLAY_LOG_LEVEL);
+
+#define AUXDISPLAY_HD44780_BACKLIGHT_MIN 0
+#define AUXDISPLAY_HD44780_BACKLIGHT_MAX 1
+
+#define AUXDISPLAY_HD44780_CUSTOM_CHARACTERS 8
+#define AUXDISPLAY_HD44780_CUSTOM_CHARACTER_WIDTH 5
+#define AUXDISPLAY_HD44780_CUSTOM_CHARACTER_HEIGHT 8
+
+enum {
+ AUXDISPLAY_HD44780_MODE_4_BIT = 0,
+ AUXDISPLAY_HD44780_MODE_8_BIT = 1,
+
+ /* Reserved for internal driver use only */
+ AUXDISPLAY_HD44780_MODE_4_BIT_ONCE,
+};
+
+/* Display commands */
+#define AUXDISPLAY_HD44780_CMD_CLEAR 0x01
+#define AUXDISPLAY_HD44780_CMD_ENTRY_MODE 0x04
+#define AUXDISPLAY_HD44780_CMD_DISPLAY_MODE 0x08
+#define AUXDISPLAY_HD44780_CMD_CGRAM_SET 0x40
+#define AUXDISPLAY_HD44780_CMD_POSITION_SET 0x80
+#define AUXDISPLAY_HD44780_CMD_SETUP 0x20
+
+#define AUXDISPLAY_HD44780_8_BIT_CONFIG 0x10
+#define AUXDISPLAY_HD44780_2_LINE_CONFIG 0x08
+
+#define AUXDISPLAY_HD44780_POSITION_BLINK_ENABLED 0x01
+#define AUXDISPLAY_HD44780_CURSOR_ENABLED 0x02
+#define AUXDISPLAY_HD44780_DISPLAY_ENABLED 0x04
+
+#define AUXDISPLAY_HD44780_DISPLAY_SHIFT 0x01
+#define AUXDISPLAY_HD44780_CURSOR_MOVE_RIGHT 0x02
+
+struct auxdisplay_hd44780_data {
+ uint16_t character_x;
+ uint16_t character_y;
+ bool cursor_enabled;
+ bool position_blink_enabled;
+ uint8_t direction;
+ bool display_shift;
+ bool backlight_state;
+};
+
+struct auxdisplay_hd44780_config {
+ struct auxdisplay_capabilities capabilities;
+ struct gpio_dt_spec rs_gpio;
+ struct gpio_dt_spec rw_gpio;
+ struct gpio_dt_spec e_gpio;
+ struct gpio_dt_spec db_gpios[8];
+ struct gpio_dt_spec backlight_gpio;
+ uint8_t line_addresses[4];
+ uint16_t enable_line_rise_delay;
+ uint16_t enable_line_fall_delay;
+ uint16_t clear_delay;
+ uint16_t boot_delay;
+};
+
+static void auxdisplay_hd44780_set_entry_mode(const struct device *dev);
+static void auxdisplay_hd44780_set_display_mode(const struct device *dev, bool enabled);
+
+static void auxdisplay_hd44780_command(const struct device *dev, bool rs, uint8_t cmd,
+ uint8_t mode)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ int8_t i = 7;
+
+ if (mode == AUXDISPLAY_HD44780_MODE_8_BIT) {
+ while (i >= 0) {
+ gpio_pin_set_dt(&config->db_gpios[i], ((cmd & BIT(i)) ? 1 : 0));
+ --i;
+ }
+ } else {
+ while (i >= 4) {
+ gpio_pin_set_dt(&config->db_gpios[i], ((cmd & BIT(i)) ? 1 : 0));
+ --i;
+ }
+ }
+
+ gpio_pin_set_dt(&config->rs_gpio, rs);
+
+ if (config->rw_gpio.port) {
+ gpio_pin_set_dt(&config->rw_gpio, 0);
+ }
+
+ gpio_pin_set_dt(&config->e_gpio, 1);
+ k_sleep(K_USEC(config->enable_line_rise_delay));
+ gpio_pin_set_dt(&config->e_gpio, 0);
+ k_sleep(K_USEC(config->enable_line_fall_delay));
+
+ if (mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
+ while (i >= 0) {
+ gpio_pin_set_dt(&config->db_gpios[(i + 4)], ((cmd & BIT(i)) ? 1 : 0));
+ --i;
+ }
+
+ gpio_pin_set_dt(&config->e_gpio, 1);
+ k_sleep(K_USEC(config->enable_line_rise_delay));
+ gpio_pin_set_dt(&config->e_gpio, 0);
+ k_sleep(K_USEC(config->enable_line_fall_delay));
+ }
+}
+
+static int auxdisplay_hd44780_init(const struct device *dev)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+ int rc;
+ uint8_t i = 0;
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_SETUP | AUXDISPLAY_HD44780_8_BIT_CONFIG;
+
+ if (config->capabilities.mode > AUXDISPLAY_HD44780_MODE_8_BIT) {
+ /* This index is reserved for internal driver usage */
+ LOG_ERR("HD44780 mode must be 4 or 8-bit");
+ return -EINVAL;
+ }
+
+ /* Configure and set GPIOs */
+ rc = gpio_pin_configure_dt(&config->rs_gpio, GPIO_OUTPUT);
+
+ if (rc < 0) {
+ LOG_ERR("Configuration of RS GPIO failed: %d", rc);
+ return rc;
+ }
+
+ if (config->rw_gpio.port) {
+ rc = gpio_pin_configure_dt(&config->rw_gpio, GPIO_OUTPUT);
+
+ if (rc < 0) {
+ LOG_ERR("Configuration of RW GPIO failed: %d", rc);
+ return rc;
+ }
+ }
+
+ rc = gpio_pin_configure_dt(&config->e_gpio, GPIO_OUTPUT);
+
+ if (rc < 0) {
+ LOG_ERR("Configuration of E GPIO failed: %d", rc);
+ return rc;
+ }
+
+ if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
+ i = 4;
+ }
+
+ while (i < 8) {
+ if (config->db_gpios[i].port) {
+ rc = gpio_pin_configure_dt(&config->db_gpios[i], GPIO_OUTPUT);
+
+ if (rc < 0) {
+ LOG_ERR("Configuration of DB%d GPIO failed: %d", i, rc);
+ return rc;
+ }
+ } else if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT && i > 3) {
+ /* Required pin missing */
+ LOG_ERR("Required DB%d pin missing (DB4-DB7 needed for 4-bit mode)", i);
+ return -EINVAL;
+ } else if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_8_BIT) {
+ /* Required pin missing */
+ LOG_ERR("Required DB%d pin missing", i);
+ return -EINVAL;
+ }
+
+ ++i;
+ }
+
+ if (config->backlight_gpio.port) {
+ rc = gpio_pin_configure_dt(&config->backlight_gpio, GPIO_OUTPUT);
+
+ if (rc < 0) {
+ LOG_ERR("Configuration of backlight GPIO failed: %d", rc);
+ return rc;
+ }
+
+ gpio_pin_set_dt(&config->backlight_gpio, 0);
+ }
+
+ data->character_x = 0;
+ data->character_y = 0;
+ data->backlight_state = false;
+ data->cursor_enabled = false;
+ data->position_blink_enabled = false;
+ data->direction = AUXDISPLAY_DIRECTION_RIGHT;
+
+ if (config->boot_delay != 0) {
+ /* Boot delay is set, wait for a period of time for the LCD to become ready to
+ * accept commands
+ */
+ k_sleep(K_MSEC(config->boot_delay));
+ }
+
+ if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
+ /* Reset display to known state in 8-bit mode */
+ auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
+ auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
+
+ /* Put display into 4-bit mode */
+ cmd = AUXDISPLAY_HD44780_CMD_SETUP;
+ auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
+ }
+
+ if (config->capabilities.rows > 1) {
+ cmd |= AUXDISPLAY_HD44780_2_LINE_CONFIG;
+ }
+
+ /* Configure display */
+ auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
+ auxdisplay_hd44780_set_display_mode(dev, true);
+ auxdisplay_hd44780_set_entry_mode(dev);
+ auxdisplay_hd44780_command(dev, false, AUXDISPLAY_HD44780_CMD_CLEAR,
+ config->capabilities.mode);
+
+ k_sleep(K_USEC(config->clear_delay));
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_capabilities_get(const struct device *dev,
+ struct auxdisplay_capabilities *capabilities)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+
+ memcpy(capabilities, &config->capabilities, sizeof(struct auxdisplay_capabilities));
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_clear(const struct device *dev)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ auxdisplay_hd44780_command(dev, false, AUXDISPLAY_HD44780_CMD_CLEAR,
+ config->capabilities.mode);
+
+ data->character_x = 0;
+ data->character_y = 0;
+
+ k_sleep(K_USEC(config->clear_delay));
+
+ return 0;
+}
+
+static void auxdisplay_hd44780_set_entry_mode(const struct device *dev)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_ENTRY_MODE;
+
+ if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
+ cmd |= AUXDISPLAY_HD44780_CURSOR_MOVE_RIGHT;
+ }
+
+ if (data->display_shift) {
+ cmd |= AUXDISPLAY_HD44780_DISPLAY_SHIFT;
+ }
+
+ auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
+}
+
+static void auxdisplay_hd44780_set_display_mode(const struct device *dev, bool enabled)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_DISPLAY_MODE;
+
+ if (data->cursor_enabled) {
+ cmd |= AUXDISPLAY_HD44780_CURSOR_ENABLED;
+ }
+
+ if (data->position_blink_enabled) {
+ cmd |= AUXDISPLAY_HD44780_POSITION_BLINK_ENABLED;
+ }
+
+ if (enabled) {
+ cmd |= AUXDISPLAY_HD44780_DISPLAY_ENABLED;
+ }
+
+ auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
+}
+
+static int auxdisplay_hd44780_display_on(const struct device *dev)
+{
+ auxdisplay_hd44780_set_display_mode(dev, true);
+ return 0;
+}
+
+static int auxdisplay_hd44780_display_off(const struct device *dev)
+{
+ auxdisplay_hd44780_set_display_mode(dev, false);
+ return 0;
+}
+
+static int auxdisplay_hd44780_cursor_set_enabled(const struct device *dev, bool enabled)
+{
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ data->cursor_enabled = enabled;
+
+ auxdisplay_hd44780_set_display_mode(dev, true);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_position_blinking_set_enabled(const struct device *dev, bool enabled)
+{
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ data->position_blink_enabled = enabled;
+
+ auxdisplay_hd44780_set_display_mode(dev, true);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_cursor_shift_set(const struct device *dev, uint8_t direction,
+ bool display_shift)
+{
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ if (display_shift) {
+ /* Not currently supported */
+ return -EINVAL;
+ }
+
+ data->direction = direction;
+ data->display_shift = (display_shift ? true : false);
+
+ auxdisplay_hd44780_set_entry_mode(dev);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_cursor_position_set(const struct device *dev,
+ enum auxdisplay_position type, int16_t x,
+ int16_t y)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET;
+
+ if (type == AUXDISPLAY_POSITION_RELATIVE) {
+ x += (int16_t)data->character_x;
+ y += (int16_t)data->character_y;
+ } else if (type == AUXDISPLAY_POSITION_RELATIVE_DIRECTION) {
+ if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
+ x += (int16_t)data->character_x;
+ y += (int16_t)data->character_y;
+ } else {
+ x -= (int16_t)data->character_x;
+ y -= (int16_t)data->character_y;
+ }
+ }
+
+ /* Check position is valid before applying */
+ if (x < 0 || y < 0) {
+ return -EINVAL;
+ } else if (x >= config->capabilities.columns || y >= config->capabilities.rows) {
+ return -EINVAL;
+ }
+
+ data->character_x = (uint16_t)x;
+ data->character_y = (uint16_t)y;
+ cmd |= config->line_addresses[data->character_y] + data->character_x;
+
+ auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_cursor_position_get(const struct device *dev, int16_t *x, int16_t *y)
+{
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ *x = (int16_t)data->character_x;
+ *y = (int16_t)data->character_y;
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_backlight_get(const struct device *dev, uint8_t *backlight)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ if (!config->backlight_gpio.port) {
+ return -ENOTSUP;
+ }
+
+ *backlight = (data->backlight_state == true ? 1 : 0);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_backlight_set(const struct device *dev, uint8_t backlight)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+
+ if (!config->backlight_gpio.port) {
+ return -ENOTSUP;
+ }
+
+ data->backlight_state = (bool)backlight;
+
+ gpio_pin_set_dt(&config->backlight_gpio, (uint8_t)data->backlight_state);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_custom_character_set(const struct device *dev,
+ struct auxdisplay_character *character)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+ uint8_t i = 0;
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_CGRAM_SET | (character->index << 3);
+
+ auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
+
+ /* HD44780 accepts 5x8 font but needs 8x8 data to be sent, mask off top 3 bits
+ * for each line sent
+ */
+ while (i < 8) {
+ uint8_t l = 0;
+
+ cmd = 0;
+
+ while (l < 5) {
+ if (character->data[(i * 5) + (4 - l)]) {
+ cmd |= BIT(l);
+ }
+
+ ++l;
+ }
+
+ auxdisplay_hd44780_command(dev, true, cmd, config->capabilities.mode);
+
+ ++i;
+ }
+
+ character->character_code = character->index;
+
+ /* Send last known address to switch back to DDRAM entry mode */
+ cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
+ (config->line_addresses[data->character_y] +
+ data->character_x);
+
+ auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
+
+ return 0;
+}
+
+static int auxdisplay_hd44780_write(const struct device *dev, const uint8_t *text, uint16_t len)
+{
+ const struct auxdisplay_hd44780_config *config = dev->config;
+ struct auxdisplay_hd44780_data *data = dev->data;
+ uint16_t i = 0;
+
+ while (i < len) {
+ auxdisplay_hd44780_command(dev, true, text[i], config->capabilities.mode);
+ ++i;
+
+ if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
+ /* Increment */
+ ++data->character_x;
+
+ if (data->character_x == config->capabilities.columns) {
+ data->character_x = 0;
+ ++data->character_y;
+
+ if (data->character_y == config->capabilities.rows) {
+ data->character_y = 0;
+ }
+
+ /* Send command to set position */
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
+ config->line_addresses[data->character_y];
+ auxdisplay_hd44780_command(dev, false, cmd,
+ config->capabilities.mode);
+ }
+ } else {
+ /* Decrement */
+ if (data->character_x == 0) {
+ data->character_x = config->capabilities.columns - 1;
+
+ if (data->character_y == 0) {
+ data->character_y = config->capabilities.rows - 1;
+ } else {
+ --data->character_y;
+ }
+
+ /* Send command to set position */
+ uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
+ (config->line_addresses[data->character_y] +
+ data->character_x);
+ auxdisplay_hd44780_command(dev, false, cmd,
+ config->capabilities.mode);
+ } else {
+ --data->character_x;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct auxdisplay_driver_api auxdisplay_hd44780_auxdisplay_api = {
+ .display_on = auxdisplay_hd44780_display_on,
+ .display_off = auxdisplay_hd44780_display_off,
+ .cursor_set_enabled = auxdisplay_hd44780_cursor_set_enabled,
+ .position_blinking_set_enabled = auxdisplay_hd44780_position_blinking_set_enabled,
+ .cursor_shift_set = auxdisplay_hd44780_cursor_shift_set,
+ .cursor_position_set = auxdisplay_hd44780_cursor_position_set,
+ .cursor_position_get = auxdisplay_hd44780_cursor_position_get,
+ .capabilities_get = auxdisplay_hd44780_capabilities_get,
+ .clear = auxdisplay_hd44780_clear,
+ .backlight_get = auxdisplay_hd44780_backlight_get,
+ .backlight_set = auxdisplay_hd44780_backlight_set,
+ .custom_character_set = auxdisplay_hd44780_custom_character_set,
+ .write = auxdisplay_hd44780_write,
+};
+
+/* Returns desired value if backlight is enabled, otherwise returns not supported value */
+#define BACKLIGHT_CHECK(inst, value) \
+ COND_CODE_1(DT_PROP_HAS_IDX(DT_DRV_INST(inst), backlight_gpios, 0), (value), \
+ (AUXDISPLAY_LIGHT_NOT_SUPPORTED))
+
+#define AUXDISPLAY_HD44780_DEVICE(inst) \
+ static struct auxdisplay_hd44780_data auxdisplay_hd44780_data_##inst; \
+ static const struct auxdisplay_hd44780_config auxdisplay_hd44780_config_##inst = { \
+ .capabilities = { \
+ .columns = DT_INST_PROP(inst, columns), \
+ .rows = DT_INST_PROP(inst, rows), \
+ .mode = DT_INST_ENUM_IDX(inst, mode), \
+ .brightness.minimum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \
+ .brightness.maximum = AUXDISPLAY_LIGHT_NOT_SUPPORTED, \
+ .backlight.minimum = BACKLIGHT_CHECK(inst, \
+ AUXDISPLAY_HD44780_BACKLIGHT_MIN), \
+ .backlight.maximum = BACKLIGHT_CHECK(inst, \
+ AUXDISPLAY_HD44780_BACKLIGHT_MAX), \
+ .custom_characters = AUXDISPLAY_HD44780_CUSTOM_CHARACTERS, \
+ .custom_character_width = AUXDISPLAY_HD44780_CUSTOM_CHARACTER_WIDTH, \
+ .custom_character_height = AUXDISPLAY_HD44780_CUSTOM_CHARACTER_HEIGHT, \
+ }, \
+ .rs_gpio = GPIO_DT_SPEC_INST_GET(inst, register_select_gpios), \
+ .rw_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, read_write_gpios, {0}), \
+ .e_gpio = GPIO_DT_SPEC_INST_GET(inst, enable_gpios), \
+ .db_gpios[0] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 0, {0}), \
+ .db_gpios[1] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 1, {0}), \
+ .db_gpios[2] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 2, {0}), \
+ .db_gpios[3] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 3, {0}), \
+ .db_gpios[4] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 4), \
+ .db_gpios[5] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 5), \
+ .db_gpios[6] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 6), \
+ .db_gpios[7] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 7), \
+ .line_addresses[0] = DT_INST_PROP_BY_IDX(inst, line_addresses, 0), \
+ .line_addresses[1] = DT_INST_PROP_BY_IDX(inst, line_addresses, 1), \
+ .line_addresses[2] = DT_INST_PROP_BY_IDX(inst, line_addresses, 2), \
+ .line_addresses[3] = DT_INST_PROP_BY_IDX(inst, line_addresses, 3), \
+ .backlight_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, backlight_gpios, {0}), \
+ .enable_line_rise_delay = DT_INST_PROP(inst, enable_line_rise_delay_us), \
+ .enable_line_fall_delay = DT_INST_PROP(inst, enable_line_fall_delay_us), \
+ .clear_delay = DT_INST_PROP(inst, clear_command_delay_us), \
+ .boot_delay = DT_INST_PROP(inst, boot_delay_ms), \
+ }; \
+ DEVICE_DT_INST_DEFINE(inst, \
+ &auxdisplay_hd44780_init, \
+ NULL, \
+ &auxdisplay_hd44780_data_##inst, \
+ &auxdisplay_hd44780_config_##inst, \
+ POST_KERNEL, \
+ CONFIG_AUXDISPLAY_INIT_PRIORITY, \
+ &auxdisplay_hd44780_auxdisplay_api);
+
+DT_INST_FOREACH_STATUS_OKAY(AUXDISPLAY_HD44780_DEVICE)
diff --git a/dts/bindings/auxdisplay/hit,hd44780.yaml b/dts/bindings/auxdisplay/hit,hd44780.yaml
new file mode 100644
index 0000000..dcf3359
--- /dev/null
+++ b/dts/bindings/auxdisplay/hit,hd44780.yaml
@@ -0,0 +1,88 @@
+#
+# Copyright (c) 2023 Jamie McCrae
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+description: Hitachi HD44780 LCD
+
+compatible: "hit,hd44780"
+
+include: [auxdisplay-device.yaml]
+
+properties:
+ mode:
+ type: int
+ required: true
+ description: Operating mode of display, 8-bit or 4 for 4-bit mode
+ enum:
+ - 4
+ - 8
+
+ register-select-gpios:
+ type: phandle-array
+ required: true
+ description: GPIO connected to Register Select (RS) of LCD
+
+ read-write-gpios:
+ type: phandle-array
+ description: Optional GPIO used for selecting read or write mode
+
+ enable-gpios:
+ type: phandle-array
+ required: true
+ description: GPIO used for enabling commands to be sent
+
+ data-bus-gpios:
+ type: phandle-array
+ required: true
+ description: |
+ Array of GPIOs connected to the DB lines of the display, this must
+ contain 8 entries ascending from DB0 to DB7, for 4-bit interface
+ displays, the first 4 must be set as `<0>`
+
+ brightness-gpios:
+ type: phandle-array
+ description: Optional GPIO used for controlling the brightness (contrast)
+
+ backlight-gpios:
+ type: phandle-array
+ description: Optional GPIO used for enabling the backlight
+
+ line-addresses:
+ type: uint8-array
+ default: [0x00, 0x40, 0x14, 0x54]
+ description: |
+ Array of addresses for each row, will use defaults if not provided.
+ Default is as per Hitachi HD44780 specification.
+
+ enable-line-rise-delay-us:
+ type: int
+ default: 800
+ description: |
+ Delay time (in us) to wait after enable line rises before setting low.
+ Default is as per Hitachi HD44780 specification.
+
+ enable-line-fall-delay-us:
+ type: int
+ default: 100
+ description: |
+ Delay time (in us) to wait after enable line falls before sending
+ another command. Default is as per Hitachi HD44780 specification.
+
+ clear-command-delay-us:
+ type: int
+ default: 5000
+ description: |
+ Delay time (in us) to wait after issuing a clear command before sending
+ another command. Default is as per Hitachi HD44780 specification.
+
+ boot-delay-ms:
+ type: int
+ default: 0
+ description: |
+ Delay time (in ms) to wait at boot time before sending a command (note:
+ this will delay startup of the whole application by this time, this
+ should only be used when time is needed for the display device to be
+ ready before it can be configured which without any delay would cause
+ the display to not function properly).