Lines Matching +full:lcd +full:- +full:backlight

1 // SPDX-License-Identifier: GPL-2.0+
3 * Character LCD driver for Linux
5 * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
6 * Copyright (C) 2016-2017 Glider bvba
28 /* Keep the backlight on this many seconds for each flash */
35 #define LCD_FLAG_N 0x0040 /* 2-rows mode */
36 #define LCD_FLAG_L 0x0080 /* Backlight enabled */
38 /* LCD commands */
62 #define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
66 struct charlcd lcd; member
74 /* contains the LCD config state */
77 /* Contains the LCD X and Y offset */
83 /* Current escape sequence and it's length or -1 if outside */
92 #define charlcd_to_priv(p) container_of(p, struct charlcd_priv, lcd)
94 /* Device single-open policy control */
103 /* turn the backlight on or off */
104 static void charlcd_backlight(struct charlcd *lcd, int on) in charlcd_backlight() argument
106 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_backlight()
108 if (!lcd->ops->backlight) in charlcd_backlight()
111 mutex_lock(&priv->bl_tempo_lock); in charlcd_backlight()
112 if (!priv->bl_tempo) in charlcd_backlight()
113 lcd->ops->backlight(lcd, on); in charlcd_backlight()
114 mutex_unlock(&priv->bl_tempo_lock); in charlcd_backlight()
123 mutex_lock(&priv->bl_tempo_lock); in charlcd_bl_off()
124 if (priv->bl_tempo) { in charlcd_bl_off()
125 priv->bl_tempo = false; in charlcd_bl_off()
126 if (!(priv->flags & LCD_FLAG_L)) in charlcd_bl_off()
127 priv->lcd.ops->backlight(&priv->lcd, 0); in charlcd_bl_off()
129 mutex_unlock(&priv->bl_tempo_lock); in charlcd_bl_off()
132 /* turn the backlight on for a little while */
133 void charlcd_poke(struct charlcd *lcd) in charlcd_poke() argument
135 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_poke()
137 if (!lcd->ops->backlight) in charlcd_poke()
140 cancel_delayed_work_sync(&priv->bl_work); in charlcd_poke()
142 mutex_lock(&priv->bl_tempo_lock); in charlcd_poke()
143 if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L)) in charlcd_poke()
144 lcd->ops->backlight(lcd, 1); in charlcd_poke()
145 priv->bl_tempo = true; in charlcd_poke()
146 schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ); in charlcd_poke()
147 mutex_unlock(&priv->bl_tempo_lock); in charlcd_poke()
151 static void charlcd_gotoxy(struct charlcd *lcd) in charlcd_gotoxy() argument
153 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_gotoxy()
160 addr = priv->addr.x < lcd->bwidth ? priv->addr.x & (lcd->hwidth - 1) in charlcd_gotoxy()
161 : lcd->bwidth - 1; in charlcd_gotoxy()
162 if (priv->addr.y & 1) in charlcd_gotoxy()
163 addr += lcd->hwidth; in charlcd_gotoxy()
164 if (priv->addr.y & 2) in charlcd_gotoxy()
165 addr += lcd->bwidth; in charlcd_gotoxy()
166 lcd->ops->write_cmd(lcd, LCD_CMD_SET_DDRAM_ADDR | addr); in charlcd_gotoxy()
169 static void charlcd_home(struct charlcd *lcd) in charlcd_home() argument
171 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_home()
173 priv->addr.x = 0; in charlcd_home()
174 priv->addr.y = 0; in charlcd_home()
175 charlcd_gotoxy(lcd); in charlcd_home()
178 static void charlcd_print(struct charlcd *lcd, char c) in charlcd_print() argument
180 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_print()
182 if (priv->addr.x < lcd->bwidth) { in charlcd_print()
183 if (lcd->char_conv) in charlcd_print()
184 c = lcd->char_conv[(unsigned char)c]; in charlcd_print()
185 lcd->ops->write_data(lcd, c); in charlcd_print()
186 priv->addr.x++; in charlcd_print()
189 if (priv->addr.x == lcd->bwidth) in charlcd_print()
190 charlcd_gotoxy(lcd); in charlcd_print()
194 static void charlcd_clear_fast(struct charlcd *lcd) in charlcd_clear_fast() argument
198 charlcd_home(lcd); in charlcd_clear_fast()
200 if (lcd->ops->clear_fast) in charlcd_clear_fast()
201 lcd->ops->clear_fast(lcd); in charlcd_clear_fast()
203 for (pos = 0; pos < min(2, lcd->height) * lcd->hwidth; pos++) in charlcd_clear_fast()
204 lcd->ops->write_data(lcd, ' '); in charlcd_clear_fast()
206 charlcd_home(lcd); in charlcd_clear_fast()
210 static void charlcd_clear_display(struct charlcd *lcd) in charlcd_clear_display() argument
212 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_clear_display()
214 lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CLEAR); in charlcd_clear_display()
215 priv->addr.x = 0; in charlcd_clear_display()
216 priv->addr.y = 0; in charlcd_clear_display()
221 static int charlcd_init_display(struct charlcd *lcd) in charlcd_init_display() argument
223 void (*write_cmd_raw)(struct charlcd *lcd, int cmd); in charlcd_init_display()
224 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_init_display()
227 if (lcd->ifwidth != 4 && lcd->ifwidth != 8) in charlcd_init_display()
228 return -EINVAL; in charlcd_init_display()
230 priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D | in charlcd_init_display()
233 long_sleep(20); /* wait 20 ms after power-up for the paranoid */ in charlcd_init_display()
236 * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure in charlcd_init_display()
237 * the LCD is in 8-bit mode afterwards in charlcd_init_display()
240 if (lcd->ifwidth == 4) { in charlcd_init_display()
242 write_cmd_raw = lcd->ops->write_cmd_raw4; in charlcd_init_display()
244 write_cmd_raw = lcd->ops->write_cmd; in charlcd_init_display()
246 write_cmd_raw(lcd, init); in charlcd_init_display()
248 write_cmd_raw(lcd, init); in charlcd_init_display()
250 write_cmd_raw(lcd, init); in charlcd_init_display()
253 if (lcd->ifwidth == 4) { in charlcd_init_display()
254 /* Switch to 4-bit mode, 1 line, small fonts */ in charlcd_init_display()
255 lcd->ops->write_cmd_raw4(lcd, LCD_CMD_FUNCTION_SET >> 4); in charlcd_init_display()
260 lcd->ops->write_cmd(lcd, in charlcd_init_display()
262 ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | in charlcd_init_display()
263 ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) | in charlcd_init_display()
264 ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)); in charlcd_init_display()
268 lcd->ops->write_cmd(lcd, LCD_CMD_DISPLAY_CTRL); in charlcd_init_display()
271 lcd->ops->write_cmd(lcd, in charlcd_init_display()
273 ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) | in charlcd_init_display()
274 ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) | in charlcd_init_display()
275 ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)); in charlcd_init_display()
277 charlcd_backlight(lcd, (priv->flags & LCD_FLAG_L) ? 1 : 0); in charlcd_init_display()
282 lcd->ops->write_cmd(lcd, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC); in charlcd_init_display()
284 charlcd_clear_display(lcd); in charlcd_init_display()
290 * any number of subcommands of the form "(x|y)[0-9]+".
296 * - ";" returns (<original x>, <original y>).
297 * - "x1;" returns (1, <original y>).
298 * - "y2x1;" returns (1, 2).
299 * - "x12y34x56;" returns (56, 34).
300 * - "" fails.
301 * - "x" fails.
302 * - "x;" fails.
303 * - "x1" fails.
304 * - "xy12;" fails.
305 * - "x12yy12;" fails.
306 * - "xx" fails.
342 * These are the file operation function for user access to /dev/lcd
348 static inline int handle_lcd_special_code(struct charlcd *lcd) in handle_lcd_special_code() argument
350 struct charlcd_priv *priv = charlcd_to_priv(lcd); in handle_lcd_special_code()
352 /* LCD special codes */ in handle_lcd_special_code()
356 char *esc = priv->esc_seq.buf + 2; in handle_lcd_special_code()
357 int oldflags = priv->flags; in handle_lcd_special_code()
362 priv->flags |= LCD_FLAG_D; in handle_lcd_special_code()
366 priv->flags &= ~LCD_FLAG_D; in handle_lcd_special_code()
370 priv->flags |= LCD_FLAG_C; in handle_lcd_special_code()
374 priv->flags &= ~LCD_FLAG_C; in handle_lcd_special_code()
378 priv->flags |= LCD_FLAG_B; in handle_lcd_special_code()
382 priv->flags &= ~LCD_FLAG_B; in handle_lcd_special_code()
386 priv->flags |= LCD_FLAG_L; in handle_lcd_special_code()
389 case '-': /* Back light OFF */ in handle_lcd_special_code()
390 priv->flags &= ~LCD_FLAG_L; in handle_lcd_special_code()
394 charlcd_poke(lcd); in handle_lcd_special_code()
398 priv->flags &= ~LCD_FLAG_F; in handle_lcd_special_code()
402 priv->flags |= LCD_FLAG_F; in handle_lcd_special_code()
406 priv->flags &= ~LCD_FLAG_N; in handle_lcd_special_code()
410 priv->flags |= LCD_FLAG_N; in handle_lcd_special_code()
414 if (priv->addr.x > 0) { in handle_lcd_special_code()
416 if (priv->addr.x < lcd->bwidth) in handle_lcd_special_code()
417 lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT); in handle_lcd_special_code()
418 priv->addr.x--; in handle_lcd_special_code()
423 if (priv->addr.x < lcd->width) { in handle_lcd_special_code()
425 if (priv->addr.x < (lcd->bwidth - 1)) in handle_lcd_special_code()
426 lcd->ops->write_cmd(lcd, in handle_lcd_special_code()
428 priv->addr.x++; in handle_lcd_special_code()
433 lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT | LCD_CMD_DISPLAY_SHIFT); in handle_lcd_special_code()
437 lcd->ops->write_cmd(lcd, in handle_lcd_special_code()
445 for (x = priv->addr.x; x < lcd->bwidth; x++) in handle_lcd_special_code()
446 lcd->ops->write_data(lcd, ' '); in handle_lcd_special_code()
449 charlcd_gotoxy(lcd); in handle_lcd_special_code()
454 charlcd_init_display(lcd); in handle_lcd_special_code()
478 cgaddr = *(esc++) - '0'; in handle_lcd_special_code()
503 lcd->ops->write_cmd(lcd, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8)); in handle_lcd_special_code()
505 lcd->ops->write_data(lcd, cgbytes[addr]); in handle_lcd_special_code()
508 charlcd_gotoxy(lcd); in handle_lcd_special_code()
514 if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';') in handle_lcd_special_code()
518 if (parse_xy(esc, &priv->addr.x, &priv->addr.y)) in handle_lcd_special_code()
519 charlcd_gotoxy(lcd); in handle_lcd_special_code()
528 if (oldflags == priv->flags) in handle_lcd_special_code()
532 if ((oldflags ^ priv->flags) & in handle_lcd_special_code()
535 lcd->ops->write_cmd(lcd, in handle_lcd_special_code()
537 ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) | in handle_lcd_special_code()
538 ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) | in handle_lcd_special_code()
539 ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0)); in handle_lcd_special_code()
541 else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N)) in handle_lcd_special_code()
542 lcd->ops->write_cmd(lcd, in handle_lcd_special_code()
544 ((lcd->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) | in handle_lcd_special_code()
545 ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) | in handle_lcd_special_code()
546 ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0)); in handle_lcd_special_code()
548 else if ((oldflags ^ priv->flags) & LCD_FLAG_L) in handle_lcd_special_code()
549 charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L)); in handle_lcd_special_code()
554 static void charlcd_write_char(struct charlcd *lcd, char c) in charlcd_write_char() argument
556 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_write_char()
559 if ((c != '\n') && priv->esc_seq.len >= 0) { in charlcd_write_char()
561 priv->esc_seq.buf[priv->esc_seq.len++] = c; in charlcd_write_char()
562 priv->esc_seq.buf[priv->esc_seq.len] = '\0'; in charlcd_write_char()
565 priv->esc_seq.len = -1; in charlcd_write_char()
570 priv->esc_seq.len = 0; in charlcd_write_char()
571 priv->esc_seq.buf[priv->esc_seq.len] = '\0'; in charlcd_write_char()
575 if (priv->addr.x > 0) { in charlcd_write_char()
580 if (priv->addr.x < lcd->bwidth) in charlcd_write_char()
582 lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT); in charlcd_write_char()
583 priv->addr.x--; in charlcd_write_char()
586 lcd->ops->write_data(lcd, ' '); in charlcd_write_char()
588 lcd->ops->write_cmd(lcd, LCD_CMD_SHIFT); in charlcd_write_char()
592 charlcd_clear_fast(lcd); in charlcd_write_char()
599 for (; priv->addr.x < lcd->bwidth; priv->addr.x++) in charlcd_write_char()
600 lcd->ops->write_data(lcd, ' '); in charlcd_write_char()
601 priv->addr.x = 0; in charlcd_write_char()
602 priv->addr.y = (priv->addr.y + 1) % lcd->height; in charlcd_write_char()
603 charlcd_gotoxy(lcd); in charlcd_write_char()
607 priv->addr.x = 0; in charlcd_write_char()
608 charlcd_gotoxy(lcd); in charlcd_write_char()
612 charlcd_print(lcd, ' '); in charlcd_write_char()
616 charlcd_print(lcd, c); in charlcd_write_char()
625 if (priv->esc_seq.len >= 2) { in charlcd_write_char()
628 if (!strcmp(priv->esc_seq.buf, "[2J")) { in charlcd_write_char()
630 charlcd_clear_fast(lcd); in charlcd_write_char()
632 } else if (!strcmp(priv->esc_seq.buf, "[H")) { in charlcd_write_char()
634 charlcd_home(lcd); in charlcd_write_char()
638 else if ((priv->esc_seq.len >= 3) && in charlcd_write_char()
639 (priv->esc_seq.buf[0] == '[') && in charlcd_write_char()
640 (priv->esc_seq.buf[1] == 'L')) { in charlcd_write_char()
641 processed = handle_lcd_special_code(lcd); in charlcd_write_char()
644 /* LCD special escape codes */ in charlcd_write_char()
649 if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN)) in charlcd_write_char()
650 priv->esc_seq.len = -1; in charlcd_write_char()
662 for (; count-- > 0; (*ppos)++, tmp++) { in charlcd_write()
671 return -EFAULT; in charlcd_write()
676 return tmp - buf; in charlcd_write()
684 ret = -EBUSY; in charlcd_open()
688 ret = -EPERM; in charlcd_open()
689 if (file->f_mode & FMODE_READ) /* device is write-only */ in charlcd_open()
692 if (priv->must_clear) { in charlcd_open()
693 charlcd_clear_display(&priv->lcd); in charlcd_open()
694 priv->must_clear = false; in charlcd_open()
718 .name = "lcd",
722 static void charlcd_puts(struct charlcd *lcd, const char *s) in charlcd_puts() argument
727 for (; count-- > 0; tmp++) { in charlcd_puts()
735 charlcd_write_char(lcd, *tmp); in charlcd_puts()
742 #define LCD_INIT_TEXT "Linux-" UTS_RELEASE "\n"
750 #define LCD_INIT_BL "\x1b[L-"
753 /* initialize the LCD driver */
754 static int charlcd_init(struct charlcd *lcd) in charlcd_init() argument
756 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_init()
759 if (lcd->ops->backlight) { in charlcd_init()
760 mutex_init(&priv->bl_tempo_lock); in charlcd_init()
761 INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off); in charlcd_init()
767 * enable mark the LCD initialized just before. in charlcd_init()
769 ret = charlcd_init_display(lcd); in charlcd_init()
774 charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT); in charlcd_init()
777 priv->must_clear = true; in charlcd_init()
778 charlcd_home(lcd); in charlcd_init()
785 struct charlcd *lcd; in charlcd_alloc() local
791 priv->esc_seq.len = -1; in charlcd_alloc()
793 lcd = &priv->lcd; in charlcd_alloc()
794 lcd->ifwidth = 8; in charlcd_alloc()
795 lcd->bwidth = DEFAULT_LCD_BWIDTH; in charlcd_alloc()
796 lcd->hwidth = DEFAULT_LCD_HWIDTH; in charlcd_alloc()
797 lcd->drvdata = priv->drvdata; in charlcd_alloc()
799 return lcd; in charlcd_alloc()
803 void charlcd_free(struct charlcd *lcd) in charlcd_free() argument
805 kfree(charlcd_to_priv(lcd)); in charlcd_free()
812 struct charlcd *lcd = the_charlcd; in panel_notify_sys() local
816 charlcd_puts(lcd, in panel_notify_sys()
820 charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+"); in panel_notify_sys()
823 charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+"); in panel_notify_sys()
837 int charlcd_register(struct charlcd *lcd) in charlcd_register() argument
841 ret = charlcd_init(lcd); in charlcd_register()
849 the_charlcd = lcd; in charlcd_register()
855 int charlcd_unregister(struct charlcd *lcd) in charlcd_unregister() argument
857 struct charlcd_priv *priv = charlcd_to_priv(lcd); in charlcd_unregister()
860 charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-"); in charlcd_unregister()
863 if (lcd->ops->backlight) { in charlcd_unregister()
864 cancel_delayed_work_sync(&priv->bl_work); in charlcd_unregister()
865 priv->lcd.ops->backlight(&priv->lcd, 0); in charlcd_unregister()