auxdisplay: implement various hd44780_common_ functions
[linux-block.git] / drivers / auxdisplay / charlcd.c
CommitLineData
351f683b 1// SPDX-License-Identifier: GPL-2.0+
39f8ea46
GU
2/*
3 * Character LCD driver for Linux
4 *
5 * Copyright (C) 2000-2008, Willy Tarreau <w@1wt.eu>
6 * Copyright (C) 2016-2017 Glider bvba
39f8ea46
GU
7 */
8
9#include <linux/atomic.h>
b34050fa 10#include <linux/ctype.h>
39f8ea46
GU
11#include <linux/fs.h>
12#include <linux/miscdevice.h>
13#include <linux/module.h>
14#include <linux/notifier.h>
15#include <linux/reboot.h>
16#include <linux/slab.h>
17#include <linux/uaccess.h>
18#include <linux/workqueue.h>
19
20#include <generated/utsrelease.h>
21
75354284 22#include "charlcd.h"
2545c1c9 23#include "hd44780_common.h"
39f8ea46
GU
24
25/* Keep the backlight on this many seconds for each flash */
26#define LCD_BL_TEMPO_PERIOD 4
27
39f8ea46 28/* LCD commands */
39f8ea46
GU
29#define LCD_CMD_DISPLAY_CTRL 0x08 /* Display control */
30#define LCD_CMD_DISPLAY_ON 0x04 /* Set display on */
31#define LCD_CMD_CURSOR_ON 0x02 /* Set cursor on */
32#define LCD_CMD_BLINK_ON 0x01 /* Set blink on */
33
39f8ea46
GU
34#define LCD_CMD_FUNCTION_SET 0x20 /* Set function */
35#define LCD_CMD_DATA_LEN_8BITS 0x10 /* Set data length to 8 bits */
36#define LCD_CMD_TWO_LINES 0x08 /* Set to two display lines */
37#define LCD_CMD_FONT_5X10_DOTS 0x04 /* Set char font to 5x10 dots */
38
39#define LCD_CMD_SET_CGRAM_ADDR 0x40 /* Set char generator RAM address */
40
d3a2fb81 41#define LCD_CMD_SET_DDRAM_ADDR 0x80 /* Set display data RAM address */
39f8ea46
GU
42
43#define LCD_ESCAPE_LEN 24 /* Max chars for LCD escape command */
44#define LCD_ESCAPE_CHAR 27 /* Use char 27 for escape command */
45
46struct charlcd_priv {
47 struct charlcd lcd;
48
49 struct delayed_work bl_work;
50 struct mutex bl_tempo_lock; /* Protects access to bl_tempo */
51 bool bl_tempo;
52
53 bool must_clear;
54
55 /* contains the LCD config state */
56 unsigned long int flags;
57
39f8ea46
GU
58 /* Current escape sequence and it's length or -1 if outside */
59 struct {
60 char buf[LCD_ESCAPE_LEN + 1];
61 int len;
62 } esc_seq;
63
2f920c0f 64 unsigned long long drvdata[];
39f8ea46
GU
65};
66
b658a211 67#define charlcd_to_priv(p) container_of(p, struct charlcd_priv, lcd)
39f8ea46
GU
68
69/* Device single-open policy control */
70static atomic_t charlcd_available = ATOMIC_INIT(1);
71
39f8ea46 72/* turn the backlight on or off */
2bf82b5a 73void charlcd_backlight(struct charlcd *lcd, enum charlcd_onoff on)
39f8ea46 74{
b658a211 75 struct charlcd_priv *priv = charlcd_to_priv(lcd);
39f8ea46
GU
76
77 if (!lcd->ops->backlight)
78 return;
79
80 mutex_lock(&priv->bl_tempo_lock);
81 if (!priv->bl_tempo)
82 lcd->ops->backlight(lcd, on);
83 mutex_unlock(&priv->bl_tempo_lock);
84}
2bf82b5a 85EXPORT_SYMBOL_GPL(charlcd_backlight);
39f8ea46
GU
86
87static void charlcd_bl_off(struct work_struct *work)
88{
89 struct delayed_work *dwork = to_delayed_work(work);
90 struct charlcd_priv *priv =
91 container_of(dwork, struct charlcd_priv, bl_work);
92
93 mutex_lock(&priv->bl_tempo_lock);
94 if (priv->bl_tempo) {
95 priv->bl_tempo = false;
96 if (!(priv->flags & LCD_FLAG_L))
bd26b181 97 priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
39f8ea46
GU
98 }
99 mutex_unlock(&priv->bl_tempo_lock);
100}
101
102/* turn the backlight on for a little while */
103void charlcd_poke(struct charlcd *lcd)
104{
b658a211 105 struct charlcd_priv *priv = charlcd_to_priv(lcd);
39f8ea46
GU
106
107 if (!lcd->ops->backlight)
108 return;
109
110 cancel_delayed_work_sync(&priv->bl_work);
111
112 mutex_lock(&priv->bl_tempo_lock);
113 if (!priv->bl_tempo && !(priv->flags & LCD_FLAG_L))
bd26b181 114 lcd->ops->backlight(lcd, CHARLCD_ON);
39f8ea46
GU
115 priv->bl_tempo = true;
116 schedule_delayed_work(&priv->bl_work, LCD_BL_TEMPO_PERIOD * HZ);
117 mutex_unlock(&priv->bl_tempo_lock);
118}
119EXPORT_SYMBOL_GPL(charlcd_poke);
120
39f8ea46
GU
121static void charlcd_home(struct charlcd *lcd)
122{
11588b59
LP
123 lcd->addr.x = 0;
124 lcd->addr.y = 0;
88645a86 125 lcd->ops->home(lcd);
39f8ea46
GU
126}
127
128static void charlcd_print(struct charlcd *lcd, char c)
129{
d3a2fb81
LP
130 struct hd44780_common *hdc = lcd->drvdata;
131
b26deabb
LP
132 if (lcd->char_conv)
133 c = lcd->char_conv[(unsigned char)c];
39f8ea46 134
b26deabb 135 if (!lcd->ops->print(lcd, c))
11588b59 136 lcd->addr.x++;
54bc937f 137
b26deabb 138 /* prevents the cursor from wrapping onto the next line */
d3a2fb81
LP
139 if (lcd->addr.x == hdc->bwidth)
140 lcd->ops->gotoxy(lcd);
39f8ea46
GU
141}
142
143static void charlcd_clear_fast(struct charlcd *lcd)
144{
2545c1c9 145 struct hd44780_common *hdc = lcd->drvdata;
39f8ea46
GU
146 int pos;
147
148 charlcd_home(lcd);
149
150 if (lcd->ops->clear_fast)
151 lcd->ops->clear_fast(lcd);
152 else
2545c1c9 153 for (pos = 0; pos < min(2, lcd->height) * hdc->hwidth; pos++)
b26deabb 154 lcd->ops->print(lcd, ' ');
39f8ea46
GU
155
156 charlcd_home(lcd);
157}
158
b34050fa
MO
159/*
160 * Parses a movement command of the form "(.*);", where the group can be
161 * any number of subcommands of the form "(x|y)[0-9]+".
162 *
163 * Returns whether the command is valid. The position arguments are
164 * only written if the parsing was successful.
165 *
166 * For instance:
167 * - ";" returns (<original x>, <original y>).
168 * - "x1;" returns (1, <original y>).
169 * - "y2x1;" returns (1, 2).
170 * - "x12y34x56;" returns (56, 34).
171 * - "" fails.
172 * - "x" fails.
173 * - "x;" fails.
174 * - "x1" fails.
175 * - "xy12;" fails.
176 * - "x12yy12;" fails.
177 * - "xx" fails.
178 */
179static bool parse_xy(const char *s, unsigned long *x, unsigned long *y)
180{
181 unsigned long new_x = *x;
182 unsigned long new_y = *y;
d717e7da 183 char *p;
b34050fa
MO
184
185 for (;;) {
186 if (!*s)
187 return false;
188
189 if (*s == ';')
190 break;
191
192 if (*s == 'x') {
d717e7da
AS
193 new_x = simple_strtoul(s + 1, &p, 10);
194 if (p == s + 1)
b34050fa 195 return false;
d717e7da 196 s = p;
b34050fa 197 } else if (*s == 'y') {
d717e7da
AS
198 new_y = simple_strtoul(s + 1, &p, 10);
199 if (p == s + 1)
b34050fa 200 return false;
d717e7da 201 s = p;
b34050fa
MO
202 } else {
203 return false;
204 }
205 }
206
207 *x = new_x;
208 *y = new_y;
209 return true;
210}
211
39f8ea46
GU
212/*
213 * These are the file operation function for user access to /dev/lcd
214 * This function can also be called from inside the kernel, by
215 * setting file and ppos to NULL.
216 *
217 */
218
219static inline int handle_lcd_special_code(struct charlcd *lcd)
220{
b658a211 221 struct charlcd_priv *priv = charlcd_to_priv(lcd);
2545c1c9 222 struct hd44780_common *hdc = lcd->drvdata;
39f8ea46
GU
223
224 /* LCD special codes */
225
226 int processed = 0;
227
228 char *esc = priv->esc_seq.buf + 2;
229 int oldflags = priv->flags;
230
231 /* check for display mode flags */
232 switch (*esc) {
233 case 'D': /* Display ON */
234 priv->flags |= LCD_FLAG_D;
d2f2187e
LP
235 if (priv->flags != oldflags)
236 lcd->ops->display(lcd, CHARLCD_ON);
237
39f8ea46
GU
238 processed = 1;
239 break;
240 case 'd': /* Display OFF */
241 priv->flags &= ~LCD_FLAG_D;
d2f2187e
LP
242 if (priv->flags != oldflags)
243 lcd->ops->display(lcd, CHARLCD_OFF);
244
39f8ea46
GU
245 processed = 1;
246 break;
247 case 'C': /* Cursor ON */
248 priv->flags |= LCD_FLAG_C;
d2f2187e
LP
249 if (priv->flags != oldflags)
250 lcd->ops->cursor(lcd, CHARLCD_ON);
251
39f8ea46
GU
252 processed = 1;
253 break;
254 case 'c': /* Cursor OFF */
255 priv->flags &= ~LCD_FLAG_C;
d2f2187e
LP
256 if (priv->flags != oldflags)
257 lcd->ops->cursor(lcd, CHARLCD_OFF);
258
39f8ea46
GU
259 processed = 1;
260 break;
261 case 'B': /* Blink ON */
262 priv->flags |= LCD_FLAG_B;
d2f2187e
LP
263 if (priv->flags != oldflags)
264 lcd->ops->blink(lcd, CHARLCD_ON);
265
39f8ea46
GU
266 processed = 1;
267 break;
268 case 'b': /* Blink OFF */
269 priv->flags &= ~LCD_FLAG_B;
d2f2187e
LP
270 if (priv->flags != oldflags)
271 lcd->ops->blink(lcd, CHARLCD_OFF);
272
39f8ea46
GU
273 processed = 1;
274 break;
275 case '+': /* Back light ON */
276 priv->flags |= LCD_FLAG_L;
277 processed = 1;
278 break;
279 case '-': /* Back light OFF */
280 priv->flags &= ~LCD_FLAG_L;
281 processed = 1;
282 break;
283 case '*': /* Flash back light */
284 charlcd_poke(lcd);
285 processed = 1;
286 break;
287 case 'f': /* Small Font */
288 priv->flags &= ~LCD_FLAG_F;
d2f2187e
LP
289 if (priv->flags != oldflags)
290 lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_SMALL);
291
39f8ea46
GU
292 processed = 1;
293 break;
294 case 'F': /* Large Font */
295 priv->flags |= LCD_FLAG_F;
d2f2187e
LP
296 if (priv->flags != oldflags)
297 lcd->ops->fontsize(lcd, CHARLCD_FONTSIZE_LARGE);
298
39f8ea46
GU
299 processed = 1;
300 break;
301 case 'n': /* One Line */
302 priv->flags &= ~LCD_FLAG_N;
d2f2187e
LP
303 if (priv->flags != oldflags)
304 lcd->ops->lines(lcd, CHARLCD_LINES_1);
305
39f8ea46
GU
306 processed = 1;
307 break;
308 case 'N': /* Two Lines */
309 priv->flags |= LCD_FLAG_N;
d2f2187e
LP
310 if (priv->flags != oldflags)
311 lcd->ops->lines(lcd, CHARLCD_LINES_2);
312
99b9b490 313 processed = 1;
39f8ea46
GU
314 break;
315 case 'l': /* Shift Cursor Left */
11588b59 316 if (lcd->addr.x > 0) {
d2f2187e
LP
317 if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
318 lcd->addr.x--;
39f8ea46 319 }
d2f2187e 320
39f8ea46
GU
321 processed = 1;
322 break;
323 case 'r': /* shift cursor right */
11588b59 324 if (lcd->addr.x < lcd->width) {
d2f2187e
LP
325 if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_RIGHT))
326 lcd->addr.x++;
39f8ea46 327 }
d2f2187e 328
39f8ea46
GU
329 processed = 1;
330 break;
331 case 'L': /* shift display left */
d2f2187e 332 lcd->ops->shift_display(lcd, CHARLCD_SHIFT_LEFT);
39f8ea46
GU
333 processed = 1;
334 break;
335 case 'R': /* shift display right */
d2f2187e 336 lcd->ops->shift_display(lcd, CHARLCD_SHIFT_RIGHT);
39f8ea46
GU
337 processed = 1;
338 break;
339 case 'k': { /* kill end of line */
b26deabb 340 int x, xs, ys;
39f8ea46 341
b26deabb
LP
342 xs = lcd->addr.x;
343 ys = lcd->addr.y;
11588b59 344 for (x = lcd->addr.x; x < hdc->bwidth; x++)
b26deabb 345 lcd->ops->print(lcd, ' ');
39f8ea46
GU
346
347 /* restore cursor position */
b26deabb
LP
348 lcd->addr.x = xs;
349 lcd->addr.y = ys;
d3a2fb81 350 lcd->ops->gotoxy(lcd);
39f8ea46
GU
351 processed = 1;
352 break;
353 }
354 case 'I': /* reinitialize display */
01ec46df
LP
355 lcd->ops->init_display(lcd);
356 priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
357 LCD_FLAG_C | LCD_FLAG_B;
39f8ea46
GU
358 processed = 1;
359 break;
360 case 'G': {
361 /* Generator : LGcxxxxx...xx; must have <c> between '0'
362 * and '7', representing the numerical ASCII code of the
363 * redefined character, and <xx...xx> a sequence of 16
364 * hex digits representing 8 bytes for each character.
365 * Most LCDs will only use 5 lower bits of the 7 first
366 * bytes.
367 */
368
369 unsigned char cgbytes[8];
370 unsigned char cgaddr;
371 int cgoffset;
372 int shift;
373 char value;
374 int addr;
375
376 if (!strchr(esc, ';'))
377 break;
378
379 esc++;
380
381 cgaddr = *(esc++) - '0';
382 if (cgaddr > 7) {
383 processed = 1;
384 break;
385 }
386
387 cgoffset = 0;
388 shift = 0;
389 value = 0;
390 while (*esc && cgoffset < 8) {
3f03b649
AS
391 int half;
392
39f8ea46 393 shift ^= 4;
3f03b649
AS
394
395 half = hex_to_bin(*esc++);
396 if (half < 0)
39f8ea46 397 continue;
39f8ea46 398
3f03b649 399 value |= half << shift;
39f8ea46
GU
400 if (shift == 0) {
401 cgbytes[cgoffset++] = value;
402 value = 0;
403 }
39f8ea46
GU
404 }
405
2c6a82f2 406 hdc->write_cmd(hdc, LCD_CMD_SET_CGRAM_ADDR | (cgaddr * 8));
39f8ea46 407 for (addr = 0; addr < cgoffset; addr++)
71ff701b 408 hdc->write_data(hdc, cgbytes[addr]);
39f8ea46
GU
409
410 /* ensures that we stop writing to CGRAM */
d3a2fb81 411 lcd->ops->gotoxy(lcd);
39f8ea46
GU
412 processed = 1;
413 break;
414 }
415 case 'x': /* gotoxy : LxXXX[yYYY]; */
416 case 'y': /* gotoxy : LyYYY[xXXX]; */
9bc30ab8
MR
417 if (priv->esc_seq.buf[priv->esc_seq.len - 1] != ';')
418 break;
419
b34050fa 420 /* If the command is valid, move to the new address */
11588b59 421 if (parse_xy(esc, &lcd->addr.x, &lcd->addr.y))
d3a2fb81 422 lcd->ops->gotoxy(lcd);
39f8ea46 423
b34050fa 424 /* Regardless of its validity, mark as processed */
39f8ea46
GU
425 processed = 1;
426 break;
427 }
428
429 /* TODO: This indent party here got ugly, clean it! */
430 /* Check whether one flag was changed */
431 if (oldflags == priv->flags)
432 return processed;
433
434 /* check whether one of B,C,D flags were changed */
435 if ((oldflags ^ priv->flags) &
436 (LCD_FLAG_B | LCD_FLAG_C | LCD_FLAG_D))
437 /* set display mode */
2c6a82f2 438 hdc->write_cmd(hdc,
39f8ea46
GU
439 LCD_CMD_DISPLAY_CTRL |
440 ((priv->flags & LCD_FLAG_D) ? LCD_CMD_DISPLAY_ON : 0) |
441 ((priv->flags & LCD_FLAG_C) ? LCD_CMD_CURSOR_ON : 0) |
442 ((priv->flags & LCD_FLAG_B) ? LCD_CMD_BLINK_ON : 0));
443 /* check whether one of F,N flags was changed */
444 else if ((oldflags ^ priv->flags) & (LCD_FLAG_F | LCD_FLAG_N))
2c6a82f2 445 hdc->write_cmd(hdc,
ac201479 446 LCD_CMD_FUNCTION_SET |
3fc04dd7 447 ((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
39f8ea46
GU
448 ((priv->flags & LCD_FLAG_F) ? LCD_CMD_FONT_5X10_DOTS : 0) |
449 ((priv->flags & LCD_FLAG_N) ? LCD_CMD_TWO_LINES : 0));
450 /* check whether L flag was changed */
451 else if ((oldflags ^ priv->flags) & LCD_FLAG_L)
452 charlcd_backlight(lcd, !!(priv->flags & LCD_FLAG_L));
453
454 return processed;
455}
456
457static void charlcd_write_char(struct charlcd *lcd, char c)
458{
b658a211 459 struct charlcd_priv *priv = charlcd_to_priv(lcd);
2545c1c9 460 struct hd44780_common *hdc = lcd->drvdata;
39f8ea46
GU
461
462 /* first, we'll test if we're in escape mode */
463 if ((c != '\n') && priv->esc_seq.len >= 0) {
464 /* yes, let's add this char to the buffer */
465 priv->esc_seq.buf[priv->esc_seq.len++] = c;
8c483758 466 priv->esc_seq.buf[priv->esc_seq.len] = '\0';
39f8ea46
GU
467 } else {
468 /* aborts any previous escape sequence */
469 priv->esc_seq.len = -1;
470
471 switch (c) {
472 case LCD_ESCAPE_CHAR:
473 /* start of an escape sequence */
474 priv->esc_seq.len = 0;
8c483758 475 priv->esc_seq.buf[priv->esc_seq.len] = '\0';
39f8ea46
GU
476 break;
477 case '\b':
478 /* go back one char and clear it */
11588b59 479 if (lcd->addr.x > 0) {
d2f2187e
LP
480 /* back one char */
481 if (!lcd->ops->shift_cursor(lcd,
482 CHARLCD_SHIFT_LEFT))
483 lcd->addr.x--;
39f8ea46
GU
484 }
485 /* replace with a space */
d2f2187e 486 charlcd_print(lcd, ' ');
39f8ea46 487 /* back one char again */
d2f2187e
LP
488 if (!lcd->ops->shift_cursor(lcd, CHARLCD_SHIFT_LEFT))
489 lcd->addr.x--;
490
39f8ea46 491 break;
9629ccca 492 case '\f':
39f8ea46
GU
493 /* quickly clear the display */
494 charlcd_clear_fast(lcd);
495 break;
496 case '\n':
497 /*
498 * flush the remainder of the current line and
499 * go to the beginning of the next line
500 */
11588b59 501 for (; lcd->addr.x < hdc->bwidth; lcd->addr.x++)
b26deabb
LP
502 lcd->ops->print(lcd, ' ');
503
11588b59
LP
504 lcd->addr.x = 0;
505 lcd->addr.y = (lcd->addr.y + 1) % lcd->height;
d3a2fb81 506 lcd->ops->gotoxy(lcd);
39f8ea46
GU
507 break;
508 case '\r':
509 /* go to the beginning of the same line */
11588b59 510 lcd->addr.x = 0;
d3a2fb81 511 lcd->ops->gotoxy(lcd);
39f8ea46
GU
512 break;
513 case '\t':
514 /* print a space instead of the tab */
515 charlcd_print(lcd, ' ');
516 break;
517 default:
518 /* simply print this char */
519 charlcd_print(lcd, c);
520 break;
521 }
522 }
523
524 /*
525 * now we'll see if we're in an escape mode and if the current
526 * escape sequence can be understood.
527 */
528 if (priv->esc_seq.len >= 2) {
529 int processed = 0;
530
531 if (!strcmp(priv->esc_seq.buf, "[2J")) {
532 /* clear the display */
533 charlcd_clear_fast(lcd);
534 processed = 1;
535 } else if (!strcmp(priv->esc_seq.buf, "[H")) {
536 /* cursor to home */
537 charlcd_home(lcd);
538 processed = 1;
539 }
540 /* codes starting with ^[[L */
541 else if ((priv->esc_seq.len >= 3) &&
542 (priv->esc_seq.buf[0] == '[') &&
543 (priv->esc_seq.buf[1] == 'L')) {
544 processed = handle_lcd_special_code(lcd);
545 }
546
547 /* LCD special escape codes */
548 /*
549 * flush the escape sequence if it's been processed
550 * or if it is getting too long.
551 */
552 if (processed || (priv->esc_seq.len >= LCD_ESCAPE_LEN))
553 priv->esc_seq.len = -1;
554 } /* escape codes */
555}
556
557static struct charlcd *the_charlcd;
558
559static ssize_t charlcd_write(struct file *file, const char __user *buf,
560 size_t count, loff_t *ppos)
561{
562 const char __user *tmp = buf;
563 char c;
564
565 for (; count-- > 0; (*ppos)++, tmp++) {
566 if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
567 /*
568 * let's be a little nice with other processes
569 * that need some CPU
570 */
571 schedule();
572
573 if (get_user(c, tmp))
574 return -EFAULT;
575
576 charlcd_write_char(the_charlcd, c);
577 }
578
579 return tmp - buf;
580}
581
582static int charlcd_open(struct inode *inode, struct file *file)
583{
b658a211 584 struct charlcd_priv *priv = charlcd_to_priv(the_charlcd);
93dc1774 585 int ret;
39f8ea46 586
93dc1774 587 ret = -EBUSY;
39f8ea46 588 if (!atomic_dec_and_test(&charlcd_available))
93dc1774 589 goto fail; /* open only once at a time */
39f8ea46 590
93dc1774 591 ret = -EPERM;
39f8ea46 592 if (file->f_mode & FMODE_READ) /* device is write-only */
93dc1774 593 goto fail;
39f8ea46
GU
594
595 if (priv->must_clear) {
45421ffe 596 priv->lcd.ops->clear_display(&priv->lcd);
39f8ea46 597 priv->must_clear = false;
45421ffe
LP
598 priv->lcd.addr.x = 0;
599 priv->lcd.addr.y = 0;
39f8ea46
GU
600 }
601 return nonseekable_open(inode, file);
93dc1774
WT
602
603 fail:
604 atomic_inc(&charlcd_available);
605 return ret;
39f8ea46
GU
606}
607
608static int charlcd_release(struct inode *inode, struct file *file)
609{
610 atomic_inc(&charlcd_available);
611 return 0;
612}
613
614static const struct file_operations charlcd_fops = {
615 .write = charlcd_write,
616 .open = charlcd_open,
617 .release = charlcd_release,
618 .llseek = no_llseek,
619};
620
621static struct miscdevice charlcd_dev = {
622 .minor = LCD_MINOR,
623 .name = "lcd",
624 .fops = &charlcd_fops,
625};
626
627static void charlcd_puts(struct charlcd *lcd, const char *s)
628{
629 const char *tmp = s;
630 int count = strlen(s);
631
632 for (; count-- > 0; tmp++) {
633 if (!in_interrupt() && (((count + 1) & 0x1f) == 0))
634 /*
635 * let's be a little nice with other processes
636 * that need some CPU
637 */
638 schedule();
639
640 charlcd_write_char(lcd, *tmp);
641 }
642}
643
c9171722
MR
644#ifdef CONFIG_PANEL_BOOT_MESSAGE
645#define LCD_INIT_TEXT CONFIG_PANEL_BOOT_MESSAGE
646#else
647#define LCD_INIT_TEXT "Linux-" UTS_RELEASE "\n"
648#endif
649
cc5d04d8
MR
650#ifdef CONFIG_CHARLCD_BL_ON
651#define LCD_INIT_BL "\x1b[L+"
652#elif defined(CONFIG_CHARLCD_BL_FLASH)
653#define LCD_INIT_BL "\x1b[L*"
654#else
655#define LCD_INIT_BL "\x1b[L-"
656#endif
657
39f8ea46
GU
658/* initialize the LCD driver */
659static int charlcd_init(struct charlcd *lcd)
660{
b658a211 661 struct charlcd_priv *priv = charlcd_to_priv(lcd);
39f8ea46
GU
662 int ret;
663
01ec46df
LP
664 priv->flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) | LCD_FLAG_D |
665 LCD_FLAG_C | LCD_FLAG_B;
39f8ea46
GU
666 if (lcd->ops->backlight) {
667 mutex_init(&priv->bl_tempo_lock);
668 INIT_DELAYED_WORK(&priv->bl_work, charlcd_bl_off);
669 }
670
671 /*
672 * before this line, we must NOT send anything to the display.
673 * Since charlcd_init_display() needs to write data, we have to
674 * enable mark the LCD initialized just before.
675 */
01ec46df 676 ret = lcd->ops->init_display(lcd);
39f8ea46
GU
677 if (ret)
678 return ret;
679
680 /* display a short message */
cc5d04d8 681 charlcd_puts(lcd, "\x1b[Lc\x1b[Lb" LCD_INIT_BL LCD_INIT_TEXT);
c9171722 682
39f8ea46
GU
683 /* clear the display on the next device opening */
684 priv->must_clear = true;
685 charlcd_home(lcd);
686 return 0;
687}
688
2545c1c9 689struct charlcd *charlcd_alloc(void)
39f8ea46
GU
690{
691 struct charlcd_priv *priv;
692 struct charlcd *lcd;
693
2545c1c9 694 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
39f8ea46
GU
695 if (!priv)
696 return NULL;
697
698 priv->esc_seq.len = -1;
699
700 lcd = &priv->lcd;
39f8ea46
GU
701
702 return lcd;
703}
704EXPORT_SYMBOL_GPL(charlcd_alloc);
705
8e44fc85
AS
706void charlcd_free(struct charlcd *lcd)
707{
708 kfree(charlcd_to_priv(lcd));
709}
710EXPORT_SYMBOL_GPL(charlcd_free);
711
39f8ea46
GU
712static int panel_notify_sys(struct notifier_block *this, unsigned long code,
713 void *unused)
714{
715 struct charlcd *lcd = the_charlcd;
716
717 switch (code) {
718 case SYS_DOWN:
719 charlcd_puts(lcd,
720 "\x0cReloading\nSystem...\x1b[Lc\x1b[Lb\x1b[L+");
721 break;
722 case SYS_HALT:
723 charlcd_puts(lcd, "\x0cSystem Halted.\x1b[Lc\x1b[Lb\x1b[L+");
724 break;
725 case SYS_POWER_OFF:
726 charlcd_puts(lcd, "\x0cPower off.\x1b[Lc\x1b[Lb\x1b[L+");
727 break;
728 default:
729 break;
730 }
731 return NOTIFY_DONE;
732}
733
734static struct notifier_block panel_notifier = {
735 panel_notify_sys,
736 NULL,
737 0
738};
739
740int charlcd_register(struct charlcd *lcd)
741{
742 int ret;
743
744 ret = charlcd_init(lcd);
745 if (ret)
746 return ret;
747
748 ret = misc_register(&charlcd_dev);
749 if (ret)
750 return ret;
751
752 the_charlcd = lcd;
753 register_reboot_notifier(&panel_notifier);
754 return 0;
755}
756EXPORT_SYMBOL_GPL(charlcd_register);
757
758int charlcd_unregister(struct charlcd *lcd)
759{
b658a211 760 struct charlcd_priv *priv = charlcd_to_priv(lcd);
39f8ea46
GU
761
762 unregister_reboot_notifier(&panel_notifier);
763 charlcd_puts(lcd, "\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
764 misc_deregister(&charlcd_dev);
765 the_charlcd = NULL;
766 if (lcd->ops->backlight) {
767 cancel_delayed_work_sync(&priv->bl_work);
bd26b181 768 priv->lcd.ops->backlight(&priv->lcd, CHARLCD_OFF);
39f8ea46
GU
769 }
770
771 return 0;
772}
773EXPORT_SYMBOL_GPL(charlcd_unregister);
774
775MODULE_LICENSE("GPL");