Linux Audio

Check our new training course

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