1 /*
2  * Copyright (c) 2023 Jamie McCrae
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT hit_hd44780
8 
9 #include <string.h>
10 #include <zephyr/device.h>
11 #include <zephyr/devicetree.h>
12 #include <zephyr/drivers/auxdisplay.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/pm/device.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/logging/log.h>
17 
18 LOG_MODULE_REGISTER(auxdisplay_hd44780, CONFIG_AUXDISPLAY_LOG_LEVEL);
19 
20 #define AUXDISPLAY_HD44780_BACKLIGHT_MIN 0
21 #define AUXDISPLAY_HD44780_BACKLIGHT_MAX 1
22 
23 #define AUXDISPLAY_HD44780_CUSTOM_CHARACTERS 8
24 #define AUXDISPLAY_HD44780_CUSTOM_CHARACTER_WIDTH 5
25 #define AUXDISPLAY_HD44780_CUSTOM_CHARACTER_HEIGHT 8
26 
27 enum {
28 	AUXDISPLAY_HD44780_MODE_4_BIT = 0,
29 	AUXDISPLAY_HD44780_MODE_8_BIT = 1,
30 
31 	/* Reserved for internal driver use only */
32 	AUXDISPLAY_HD44780_MODE_4_BIT_ONCE,
33 };
34 
35 /* Display commands */
36 #define AUXDISPLAY_HD44780_CMD_CLEAR 0x01
37 #define AUXDISPLAY_HD44780_CMD_ENTRY_MODE 0x04
38 #define AUXDISPLAY_HD44780_CMD_DISPLAY_MODE 0x08
39 #define AUXDISPLAY_HD44780_CMD_CGRAM_SET 0x40
40 #define AUXDISPLAY_HD44780_CMD_POSITION_SET 0x80
41 #define AUXDISPLAY_HD44780_CMD_SETUP 0x20
42 
43 #define AUXDISPLAY_HD44780_8_BIT_CONFIG 0x10
44 #define AUXDISPLAY_HD44780_2_LINE_CONFIG 0x08
45 
46 #define AUXDISPLAY_HD44780_POSITION_BLINK_ENABLED 0x01
47 #define AUXDISPLAY_HD44780_CURSOR_ENABLED 0x02
48 #define AUXDISPLAY_HD44780_DISPLAY_ENABLED 0x04
49 
50 #define AUXDISPLAY_HD44780_DISPLAY_SHIFT 0x01
51 #define AUXDISPLAY_HD44780_CURSOR_MOVE_RIGHT 0x02
52 
53 struct auxdisplay_hd44780_data {
54 	uint16_t character_x;
55 	uint16_t character_y;
56 	bool cursor_enabled;
57 	bool position_blink_enabled;
58 	uint8_t direction;
59 	bool display_shift;
60 	bool backlight_state;
61 };
62 
63 struct auxdisplay_hd44780_config {
64 	struct auxdisplay_capabilities capabilities;
65 	struct gpio_dt_spec rs_gpio;
66 	struct gpio_dt_spec rw_gpio;
67 	struct gpio_dt_spec e_gpio;
68 	struct gpio_dt_spec db_gpios[8];
69 	struct gpio_dt_spec backlight_gpio;
70 	uint8_t line_addresses[4];
71 	uint16_t enable_line_rise_delay;
72 	uint16_t enable_line_fall_delay;
73 	uint16_t rs_line_delay;
74 	uint16_t clear_delay;
75 	uint16_t boot_delay;
76 };
77 
78 static void auxdisplay_hd44780_set_entry_mode(const struct device *dev);
79 static void auxdisplay_hd44780_set_display_mode(const struct device *dev, bool enabled);
80 static int auxdisplay_hd44780_clear(const struct device *dev);
81 
hd44780_pulse_enable_line(const struct device * dev)82 static void hd44780_pulse_enable_line(const struct device *dev)
83 {
84 	const struct auxdisplay_hd44780_config *const config = dev->config;
85 
86 	gpio_pin_set_dt(&config->e_gpio, 1);
87 	k_sleep(K_NSEC(config->enable_line_rise_delay));
88 	gpio_pin_set_dt(&config->e_gpio, 0);
89 	k_sleep(K_NSEC(config->enable_line_fall_delay));
90 }
91 
hd44780_set_rs_rw_lines(const struct device * dev,bool rs,bool rw)92 static inline void hd44780_set_rs_rw_lines(const struct device *dev, bool rs, bool rw)
93 {
94 	const struct auxdisplay_hd44780_config *const config = dev->config;
95 
96 	gpio_pin_set_dt(&config->rs_gpio, rs);
97 	if (config->rw_gpio.port) {
98 		gpio_pin_set_dt(&config->rw_gpio, rw);
99 	}
100 
101 	k_sleep(K_NSEC(config->rs_line_delay));
102 }
103 
hd44780_db_gpios_configure(const struct device * dev,uint8_t lsb_line,gpio_flags_t flags)104 static int hd44780_db_gpios_configure(const struct device *dev, uint8_t lsb_line,
105 				      gpio_flags_t flags)
106 {
107 	const struct auxdisplay_hd44780_config *config = dev->config;
108 	int rc;
109 
110 	for (int line = 7; line >= lsb_line; --line) {
111 		rc = gpio_pin_configure_dt(&config->db_gpios[line], flags);
112 		if (rc < 0) {
113 			return rc;
114 		}
115 	}
116 
117 	return 0;
118 }
119 
auxdisplay_hd44780_command(const struct device * dev,bool rs,uint8_t cmd,uint8_t mode)120 static void auxdisplay_hd44780_command(const struct device *dev, bool rs,
121 				       uint8_t cmd, uint8_t mode)
122 {
123 	int rc;
124 	const struct auxdisplay_hd44780_config *config = dev->config;
125 	int8_t i = 7;
126 	const int8_t lsb_line = (mode == AUXDISPLAY_HD44780_MODE_8_BIT) ? 0 : 4;
127 	int8_t ncommands = (mode == AUXDISPLAY_HD44780_MODE_4_BIT) ? 2 : 1;
128 	const bool check_busy_flag = (!config->rw_gpio.port ||
129 				      (mode == AUXDISPLAY_HD44780_MODE_4_BIT_ONCE)) ?
130 				      false : true;
131 
132 	if (check_busy_flag) {
133 		bool busy;
134 
135 		rc = hd44780_db_gpios_configure(dev, lsb_line, GPIO_INPUT | GPIO_PULL_DOWN);
136 		if (rc < 0) {
137 			LOG_ERR("Configuration of db-gpios as inputs failed: %d", rc);
138 			return;
139 		}
140 
141 		hd44780_set_rs_rw_lines(dev, 0, 1);
142 		do {
143 			hd44780_pulse_enable_line(dev);
144 
145 			/* We don't care about the other pins. */
146 			busy = gpio_pin_get_dt(&config->db_gpios[7]);
147 
148 			if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
149 				/* In this mode we have to initiate two separate readbacks. */
150 				hd44780_pulse_enable_line(dev);
151 			}
152 		} while (busy);
153 
154 		rc = hd44780_db_gpios_configure(dev, lsb_line, GPIO_OUTPUT);
155 		if (rc < 0) {
156 			LOG_ERR("Configuration of db-gpios as outputs failed: %d", rc);
157 			return;
158 		}
159 	}
160 
161 	hd44780_set_rs_rw_lines(dev, rs, 0);
162 	while (ncommands--) {
163 		for (int8_t line = 7; line >= lsb_line; --line) {
164 			gpio_pin_set_dt(&config->db_gpios[line], ((cmd & BIT(i)) ? 1 : 0));
165 			--i;
166 		}
167 
168 		hd44780_pulse_enable_line(dev);
169 	}
170 
171 	if (!check_busy_flag) {
172 		/* Sleep for a max execution time for a given instruction. */
173 		uint16_t cmd_delay_us = (cmd == AUXDISPLAY_HD44780_CMD_CLEAR) ? 1520 : 37;
174 
175 		k_sleep(K_USEC(cmd_delay_us));
176 	}
177 }
178 
hd44780_ic_initialize(const struct device * dev)179 static void hd44780_ic_initialize(const struct device *dev)
180 {
181 	const struct auxdisplay_hd44780_config *config = dev->config;
182 	uint8_t cmd;
183 
184 	/*
185 	 * If proper power supply is used to power the HD44780, it initializes correctly
186 	 * on a reset condition all by itself. However, if the power supply is below
187 	 * its expectations (e.g. supplying it with some 3.3V Nucleo board),
188 	 * it won't initialize properly on its own, and the MCU has to carry out
189 	 * the initialization as listed in the reference manual.
190 	 * Since we cannot determine it properly in the runtime,
191 	 * always carry out the initialization procedure.
192 	 */
193 	cmd = AUXDISPLAY_HD44780_CMD_SETUP | AUXDISPLAY_HD44780_8_BIT_CONFIG;
194 	auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
195 	k_sleep(K_USEC(4100));
196 	auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
197 	k_sleep(K_USEC(100));
198 	auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
199 	k_sleep(K_USEC(100));
200 
201 	if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
202 		/* Put display into 4-bit mode */
203 		cmd = AUXDISPLAY_HD44780_CMD_SETUP;
204 		auxdisplay_hd44780_command(dev, false, cmd, AUXDISPLAY_HD44780_MODE_4_BIT_ONCE);
205 	}
206 
207 	/* Configure display */
208 	if (config->capabilities.rows > 1) {
209 		cmd |= AUXDISPLAY_HD44780_2_LINE_CONFIG;
210 	}
211 	auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
212 
213 	auxdisplay_hd44780_set_display_mode(dev, false);
214 	auxdisplay_hd44780_clear(dev);
215 	auxdisplay_hd44780_set_entry_mode(dev);
216 
217 	auxdisplay_hd44780_set_display_mode(dev, true);
218 }
219 
auxdisplay_hd44780_init(const struct device * dev)220 static int auxdisplay_hd44780_init(const struct device *dev)
221 {
222 	const struct auxdisplay_hd44780_config *config = dev->config;
223 	struct auxdisplay_hd44780_data *data = dev->data;
224 	int rc;
225 	uint8_t i = 0;
226 
227 	if (config->capabilities.mode > AUXDISPLAY_HD44780_MODE_8_BIT) {
228 		/* This index is reserved for internal driver usage */
229 		LOG_ERR("HD44780 mode must be 4 or 8-bit");
230 		return -EINVAL;
231 	}
232 
233 	/* Configure and set GPIOs */
234 	rc = gpio_pin_configure_dt(&config->rs_gpio, GPIO_OUTPUT);
235 
236 	if (rc < 0) {
237 		LOG_ERR("Configuration of RS GPIO failed: %d", rc);
238 		return rc;
239 	}
240 
241 	rc = gpio_pin_configure_dt(&config->e_gpio, GPIO_OUTPUT);
242 
243 	if (rc < 0) {
244 		LOG_ERR("Configuration of E GPIO failed: %d", rc);
245 		return rc;
246 	}
247 
248 	if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT) {
249 		i = 4;
250 	}
251 
252 	while (i < 8) {
253 		if (config->db_gpios[i].port) {
254 			rc = gpio_pin_configure_dt(&config->db_gpios[i], GPIO_OUTPUT);
255 
256 			if (rc < 0) {
257 				LOG_ERR("Configuration of DB%d GPIO failed: %d", i, rc);
258 				return rc;
259 			}
260 		} else if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_4_BIT && i > 3) {
261 			/* Required pin missing */
262 			LOG_ERR("Required DB%d pin missing (DB4-DB7 needed for 4-bit mode)", i);
263 			return -EINVAL;
264 		} else if (config->capabilities.mode == AUXDISPLAY_HD44780_MODE_8_BIT) {
265 			/* Required pin missing */
266 			LOG_ERR("Required DB%d pin missing", i);
267 			return -EINVAL;
268 		}
269 
270 		++i;
271 	}
272 
273 	if (config->rw_gpio.port) {
274 		rc = gpio_pin_configure_dt(&config->rw_gpio, GPIO_OUTPUT);
275 
276 		if (rc < 0) {
277 			LOG_ERR("Configuration of RW GPIO failed: %d", rc);
278 			return rc;
279 		}
280 	}
281 
282 	if (config->backlight_gpio.port) {
283 		rc = gpio_pin_configure_dt(&config->backlight_gpio, GPIO_OUTPUT);
284 
285 		if (rc < 0) {
286 			LOG_ERR("Configuration of backlight GPIO failed: %d", rc);
287 			return rc;
288 		}
289 
290 		gpio_pin_set_dt(&config->backlight_gpio, 0);
291 	}
292 
293 	data->character_x = 0;
294 	data->character_y = 0;
295 	data->backlight_state = false;
296 	data->cursor_enabled = false;
297 	data->position_blink_enabled = false;
298 	data->direction = AUXDISPLAY_DIRECTION_RIGHT;
299 
300 	if (config->boot_delay != 0) {
301 		/* Boot delay is set, wait for a period of time for the LCD to become ready to
302 		 * accept commands
303 		 */
304 		k_sleep(K_MSEC(config->boot_delay));
305 	}
306 
307 	hd44780_ic_initialize(dev);
308 
309 	return 0;
310 }
311 
auxdisplay_hd44780_capabilities_get(const struct device * dev,struct auxdisplay_capabilities * capabilities)312 static int auxdisplay_hd44780_capabilities_get(const struct device *dev,
313 					       struct auxdisplay_capabilities *capabilities)
314 {
315 	const struct auxdisplay_hd44780_config *config = dev->config;
316 
317 	memcpy(capabilities, &config->capabilities, sizeof(struct auxdisplay_capabilities));
318 
319 	return 0;
320 }
321 
auxdisplay_hd44780_clear(const struct device * dev)322 static int auxdisplay_hd44780_clear(const struct device *dev)
323 {
324 	const struct auxdisplay_hd44780_config *config = dev->config;
325 	struct auxdisplay_hd44780_data *data = dev->data;
326 
327 	auxdisplay_hd44780_command(dev, false, AUXDISPLAY_HD44780_CMD_CLEAR,
328 				   config->capabilities.mode);
329 
330 	data->character_x = 0;
331 	data->character_y = 0;
332 
333 	k_sleep(K_USEC(config->clear_delay));
334 
335 	return 0;
336 }
337 
auxdisplay_hd44780_set_entry_mode(const struct device * dev)338 static void auxdisplay_hd44780_set_entry_mode(const struct device *dev)
339 {
340 	const struct auxdisplay_hd44780_config *config = dev->config;
341 	struct auxdisplay_hd44780_data *data = dev->data;
342 	uint8_t cmd = AUXDISPLAY_HD44780_CMD_ENTRY_MODE;
343 
344 	if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
345 		cmd |= AUXDISPLAY_HD44780_CURSOR_MOVE_RIGHT;
346 	}
347 
348 	if (data->display_shift) {
349 		cmd |= AUXDISPLAY_HD44780_DISPLAY_SHIFT;
350 	}
351 
352 	auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
353 }
354 
auxdisplay_hd44780_set_display_mode(const struct device * dev,bool enabled)355 static void auxdisplay_hd44780_set_display_mode(const struct device *dev, bool enabled)
356 {
357 	const struct auxdisplay_hd44780_config *config = dev->config;
358 	struct auxdisplay_hd44780_data *data = dev->data;
359 	uint8_t cmd = AUXDISPLAY_HD44780_CMD_DISPLAY_MODE;
360 
361 	if (data->cursor_enabled) {
362 		cmd |= AUXDISPLAY_HD44780_CURSOR_ENABLED;
363 	}
364 
365 	if (data->position_blink_enabled) {
366 		cmd |= AUXDISPLAY_HD44780_POSITION_BLINK_ENABLED;
367 	}
368 
369 	if (enabled) {
370 		cmd |= AUXDISPLAY_HD44780_DISPLAY_ENABLED;
371 	}
372 
373 	auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
374 }
375 
auxdisplay_hd44780_display_on(const struct device * dev)376 static int auxdisplay_hd44780_display_on(const struct device *dev)
377 {
378 	auxdisplay_hd44780_set_display_mode(dev, true);
379 	return 0;
380 }
381 
auxdisplay_hd44780_display_off(const struct device * dev)382 static int auxdisplay_hd44780_display_off(const struct device *dev)
383 {
384 	auxdisplay_hd44780_set_display_mode(dev, false);
385 	return 0;
386 }
387 
auxdisplay_hd44780_cursor_set_enabled(const struct device * dev,bool enabled)388 static int auxdisplay_hd44780_cursor_set_enabled(const struct device *dev, bool enabled)
389 {
390 	struct auxdisplay_hd44780_data *data = dev->data;
391 
392 	data->cursor_enabled = enabled;
393 
394 	auxdisplay_hd44780_set_display_mode(dev, true);
395 
396 	return 0;
397 }
398 
auxdisplay_hd44780_position_blinking_set_enabled(const struct device * dev,bool enabled)399 static int auxdisplay_hd44780_position_blinking_set_enabled(const struct device *dev, bool enabled)
400 {
401 	struct auxdisplay_hd44780_data *data = dev->data;
402 
403 	data->position_blink_enabled = enabled;
404 
405 	auxdisplay_hd44780_set_display_mode(dev, true);
406 
407 	return 0;
408 }
409 
auxdisplay_hd44780_cursor_shift_set(const struct device * dev,uint8_t direction,bool display_shift)410 static int auxdisplay_hd44780_cursor_shift_set(const struct device *dev, uint8_t direction,
411 					       bool display_shift)
412 {
413 	struct auxdisplay_hd44780_data *data = dev->data;
414 
415 	if (display_shift) {
416 		/* Not currently supported */
417 		return -EINVAL;
418 	}
419 
420 	data->direction = direction;
421 	data->display_shift = (display_shift ? true : false);
422 
423 	auxdisplay_hd44780_set_entry_mode(dev);
424 
425 	return 0;
426 }
427 
auxdisplay_hd44780_cursor_position_set(const struct device * dev,enum auxdisplay_position type,int16_t x,int16_t y)428 static int auxdisplay_hd44780_cursor_position_set(const struct device *dev,
429 						  enum auxdisplay_position type, int16_t x,
430 						  int16_t y)
431 {
432 	const struct auxdisplay_hd44780_config *config = dev->config;
433 	struct auxdisplay_hd44780_data *data = dev->data;
434 	uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET;
435 
436 	if (type == AUXDISPLAY_POSITION_RELATIVE) {
437 		x += (int16_t)data->character_x;
438 		y += (int16_t)data->character_y;
439 	} else if (type == AUXDISPLAY_POSITION_RELATIVE_DIRECTION) {
440 		if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
441 			x += (int16_t)data->character_x;
442 			y += (int16_t)data->character_y;
443 		} else {
444 			x -= (int16_t)data->character_x;
445 			y -= (int16_t)data->character_y;
446 		}
447 	}
448 
449 	/* Check position is valid before applying */
450 	if (x < 0 || y < 0) {
451 		return -EINVAL;
452 	} else if (x >= config->capabilities.columns || y >= config->capabilities.rows) {
453 		return -EINVAL;
454 	}
455 
456 	data->character_x = (uint16_t)x;
457 	data->character_y = (uint16_t)y;
458 	cmd |= config->line_addresses[data->character_y] + data->character_x;
459 
460 	auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
461 
462 	return 0;
463 }
464 
auxdisplay_hd44780_cursor_position_get(const struct device * dev,int16_t * x,int16_t * y)465 static int auxdisplay_hd44780_cursor_position_get(const struct device *dev, int16_t *x, int16_t *y)
466 {
467 	struct auxdisplay_hd44780_data *data = dev->data;
468 
469 	*x = (int16_t)data->character_x;
470 	*y = (int16_t)data->character_y;
471 
472 	return 0;
473 }
474 
auxdisplay_hd44780_backlight_get(const struct device * dev,uint8_t * backlight)475 static int auxdisplay_hd44780_backlight_get(const struct device *dev, uint8_t *backlight)
476 {
477 	const struct auxdisplay_hd44780_config *config = dev->config;
478 	struct auxdisplay_hd44780_data *data = dev->data;
479 
480 	if (!config->backlight_gpio.port) {
481 		return -ENOTSUP;
482 	}
483 
484 	*backlight = (data->backlight_state == true ? 1 : 0);
485 
486 	return 0;
487 }
488 
auxdisplay_hd44780_backlight_set(const struct device * dev,uint8_t backlight)489 static int auxdisplay_hd44780_backlight_set(const struct device *dev, uint8_t backlight)
490 {
491 	const struct auxdisplay_hd44780_config *config = dev->config;
492 	struct auxdisplay_hd44780_data *data = dev->data;
493 
494 	if (!config->backlight_gpio.port) {
495 		return -ENOTSUP;
496 	}
497 
498 	data->backlight_state = (bool)backlight;
499 
500 	gpio_pin_set_dt(&config->backlight_gpio, (uint8_t)data->backlight_state);
501 
502 	return 0;
503 }
504 
auxdisplay_hd44780_custom_character_set(const struct device * dev,struct auxdisplay_character * character)505 static int auxdisplay_hd44780_custom_character_set(const struct device *dev,
506 						   struct auxdisplay_character *character)
507 {
508 	const struct auxdisplay_hd44780_config *config = dev->config;
509 	struct auxdisplay_hd44780_data *data = dev->data;
510 	uint8_t i = 0;
511 	uint8_t cmd = AUXDISPLAY_HD44780_CMD_CGRAM_SET | (character->index << 3);
512 
513 	auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
514 
515 	/* HD44780 accepts 5x8 font but needs 8x8 data to be sent, mask off top 3 bits
516 	 * for each line sent
517 	 */
518 	while (i < 8) {
519 		uint8_t l = 0;
520 
521 		cmd = 0;
522 
523 		while (l < 5) {
524 			if (character->data[(i * 5) + (4 - l)]) {
525 				cmd |= BIT(l);
526 			}
527 
528 			++l;
529 		}
530 
531 		auxdisplay_hd44780_command(dev, true, cmd, config->capabilities.mode);
532 
533 		++i;
534 	}
535 
536 	character->character_code = character->index;
537 
538 	/* Send last known address to switch back to DDRAM entry mode */
539 	cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
540 	      (config->line_addresses[data->character_y] +
541 	       data->character_x);
542 
543 	auxdisplay_hd44780_command(dev, false, cmd, config->capabilities.mode);
544 
545 	return 0;
546 }
547 
auxdisplay_hd44780_write(const struct device * dev,const uint8_t * text,uint16_t len)548 static int auxdisplay_hd44780_write(const struct device *dev, const uint8_t *text, uint16_t len)
549 {
550 	const struct auxdisplay_hd44780_config *config = dev->config;
551 	struct auxdisplay_hd44780_data *data = dev->data;
552 	uint16_t i = 0;
553 
554 	while (i < len) {
555 		auxdisplay_hd44780_command(dev, true, text[i], config->capabilities.mode);
556 		++i;
557 
558 		if (data->direction == AUXDISPLAY_DIRECTION_RIGHT) {
559 			/* Increment */
560 			++data->character_x;
561 
562 			if (data->character_x == config->capabilities.columns) {
563 				data->character_x = 0;
564 				++data->character_y;
565 
566 				if (data->character_y == config->capabilities.rows) {
567 					data->character_y = 0;
568 				}
569 
570 				/* Send command to set position */
571 				uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
572 					      config->line_addresses[data->character_y];
573 				auxdisplay_hd44780_command(dev, false, cmd,
574 							config->capabilities.mode);
575 			}
576 		} else {
577 			/* Decrement */
578 			if (data->character_x == 0) {
579 				data->character_x = config->capabilities.columns - 1;
580 
581 				if (data->character_y == 0) {
582 					data->character_y = config->capabilities.rows - 1;
583 				} else {
584 					--data->character_y;
585 				}
586 
587 				/* Send command to set position */
588 				uint8_t cmd = AUXDISPLAY_HD44780_CMD_POSITION_SET |
589 					      (config->line_addresses[data->character_y] +
590 					       data->character_x);
591 				auxdisplay_hd44780_command(dev, false, cmd,
592 							config->capabilities.mode);
593 			} else {
594 				--data->character_x;
595 			}
596 		}
597 	}
598 
599 	return 0;
600 }
601 
602 static DEVICE_API(auxdisplay, auxdisplay_hd44780_auxdisplay_api) = {
603 	.display_on = auxdisplay_hd44780_display_on,
604 	.display_off = auxdisplay_hd44780_display_off,
605 	.cursor_set_enabled = auxdisplay_hd44780_cursor_set_enabled,
606 	.position_blinking_set_enabled = auxdisplay_hd44780_position_blinking_set_enabled,
607 	.cursor_shift_set = auxdisplay_hd44780_cursor_shift_set,
608 	.cursor_position_set = auxdisplay_hd44780_cursor_position_set,
609 	.cursor_position_get = auxdisplay_hd44780_cursor_position_get,
610 	.capabilities_get = auxdisplay_hd44780_capabilities_get,
611 	.clear = auxdisplay_hd44780_clear,
612 	.backlight_get = auxdisplay_hd44780_backlight_get,
613 	.backlight_set = auxdisplay_hd44780_backlight_set,
614 	.custom_character_set = auxdisplay_hd44780_custom_character_set,
615 	.write = auxdisplay_hd44780_write,
616 };
617 
618 /* Returns desired value if backlight is enabled, otherwise returns not supported value */
619 #define BACKLIGHT_CHECK(inst, value)							\
620 	COND_CODE_1(DT_PROP_HAS_IDX(DT_DRV_INST(inst), backlight_gpios, 0), (value),	\
621 		    (AUXDISPLAY_LIGHT_NOT_SUPPORTED))
622 
623 #define AUXDISPLAY_HD44780_DEVICE(inst)								\
624 	static struct auxdisplay_hd44780_data auxdisplay_hd44780_data_##inst;			\
625 	static const struct auxdisplay_hd44780_config auxdisplay_hd44780_config_##inst = {	\
626 		.capabilities = {								\
627 			.columns = DT_INST_PROP(inst, columns),					\
628 			.rows = DT_INST_PROP(inst, rows),					\
629 			.mode = DT_INST_ENUM_IDX(inst, mode),					\
630 			.brightness.minimum = AUXDISPLAY_LIGHT_NOT_SUPPORTED,			\
631 			.brightness.maximum = AUXDISPLAY_LIGHT_NOT_SUPPORTED,			\
632 			.backlight.minimum = BACKLIGHT_CHECK(inst,				\
633 							     AUXDISPLAY_HD44780_BACKLIGHT_MIN),	\
634 			.backlight.maximum = BACKLIGHT_CHECK(inst,				\
635 							     AUXDISPLAY_HD44780_BACKLIGHT_MAX),	\
636 			.custom_characters = AUXDISPLAY_HD44780_CUSTOM_CHARACTERS,		\
637 			.custom_character_width = AUXDISPLAY_HD44780_CUSTOM_CHARACTER_WIDTH,	\
638 			.custom_character_height = AUXDISPLAY_HD44780_CUSTOM_CHARACTER_HEIGHT,	\
639 		},										\
640 		.rs_gpio = GPIO_DT_SPEC_INST_GET(inst, register_select_gpios),			\
641 		.rw_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, read_write_gpios, {0}),		\
642 		.e_gpio = GPIO_DT_SPEC_INST_GET(inst, enable_gpios),				\
643 		.db_gpios[0] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 0, {0}),	\
644 		.db_gpios[1] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 1, {0}),	\
645 		.db_gpios[2] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 2, {0}),	\
646 		.db_gpios[3] = GPIO_DT_SPEC_INST_GET_BY_IDX_OR(inst, data_bus_gpios, 3, {0}),	\
647 		.db_gpios[4] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 4),		\
648 		.db_gpios[5] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 5),		\
649 		.db_gpios[6] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 6),		\
650 		.db_gpios[7] = GPIO_DT_SPEC_INST_GET_BY_IDX(inst, data_bus_gpios, 7),		\
651 		.line_addresses[0] = DT_INST_PROP_BY_IDX(inst, line_addresses, 0),		\
652 		.line_addresses[1] = DT_INST_PROP_BY_IDX(inst, line_addresses, 1),		\
653 		.line_addresses[2] = DT_INST_PROP_BY_IDX(inst, line_addresses, 2),		\
654 		.line_addresses[3] = DT_INST_PROP_BY_IDX(inst, line_addresses, 3),		\
655 		.backlight_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, backlight_gpios, {0}),		\
656 		.enable_line_rise_delay = DT_INST_PROP(inst, enable_line_rise_delay_ns),	\
657 		.enable_line_fall_delay = DT_INST_PROP(inst, enable_line_fall_delay_ns),	\
658 		.rs_line_delay = DT_INST_PROP(inst, rs_line_delay_ns),				\
659 		.clear_delay = DT_INST_PROP(inst, clear_command_delay_us),			\
660 		.boot_delay = DT_INST_PROP(inst, boot_delay_ms),				\
661 	};											\
662 	DEVICE_DT_INST_DEFINE(inst,								\
663 			&auxdisplay_hd44780_init,						\
664 			NULL,									\
665 			&auxdisplay_hd44780_data_##inst,					\
666 			&auxdisplay_hd44780_config_##inst,					\
667 			POST_KERNEL,								\
668 			CONFIG_AUXDISPLAY_INIT_PRIORITY,					\
669 			&auxdisplay_hd44780_auxdisplay_api);
670 
671 DT_INST_FOREACH_STATUS_OKAY(AUXDISPLAY_HD44780_DEVICE)
672