xref: /linux/drivers/auxdisplay/hd44780_common.c (revision 01ec46dfa633a52ccfe38d4a194460d6adb2ba00)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 #include <linux/module.h>
3 #include <linux/sched.h>
4 #include <linux/slab.h>
5 
6 #include "charlcd.h"
7 #include "hd44780_common.h"
8 
9 /* LCD commands */
10 #define LCD_CMD_DISPLAY_CLEAR	0x01	/* Clear entire display */
11 
12 #define LCD_CMD_ENTRY_MODE	0x04	/* Set entry mode */
13 #define LCD_CMD_CURSOR_INC	0x02	/* Increment cursor */
14 
15 #define LCD_CMD_DISPLAY_CTRL	0x08	/* Display control */
16 #define LCD_CMD_DISPLAY_ON	0x04	/* Set display on */
17 #define LCD_CMD_CURSOR_ON	0x02	/* Set cursor on */
18 #define LCD_CMD_BLINK_ON	0x01	/* Set blink on */
19 
20 #define LCD_CMD_FUNCTION_SET	0x20	/* Set function */
21 #define LCD_CMD_DATA_LEN_8BITS	0x10	/* Set data length to 8 bits */
22 #define LCD_CMD_TWO_LINES	0x08	/* Set to two display lines */
23 #define LCD_CMD_FONT_5X10_DOTS	0x04	/* Set char font to 5x10 dots */
24 
25 #define LCD_CMD_SET_DDRAM_ADDR	0x80	/* Set display data RAM address */
26 
27 /* sleeps that many milliseconds with a reschedule */
28 static void long_sleep(int ms)
29 {
30 	schedule_timeout_interruptible(msecs_to_jiffies(ms));
31 }
32 
33 int hd44780_common_print(struct charlcd *lcd, int c)
34 {
35 	struct hd44780_common *hdc = lcd->drvdata;
36 
37 	if (lcd->addr.x < hdc->bwidth) {
38 		hdc->write_data(hdc, c);
39 		return 0;
40 	}
41 
42 	return 1;
43 }
44 EXPORT_SYMBOL_GPL(hd44780_common_print);
45 
46 int hd44780_common_gotoxy(struct charlcd *lcd)
47 {
48 	struct hd44780_common *hdc = lcd->drvdata;
49 	unsigned int addr;
50 
51 	/*
52 	 * we force the cursor to stay at the end of the
53 	 * line if it wants to go farther
54 	 */
55 	addr = lcd->addr.x < hdc->bwidth ? lcd->addr.x & (hdc->hwidth - 1)
56 					  : hdc->bwidth - 1;
57 	if (lcd->addr.y & 1)
58 		addr += hdc->hwidth;
59 	if (lcd->addr.y & 2)
60 		addr += hdc->bwidth;
61 	hdc->write_cmd(hdc, LCD_CMD_SET_DDRAM_ADDR | addr);
62 	return 0;
63 }
64 EXPORT_SYMBOL_GPL(hd44780_common_gotoxy);
65 
66 int hd44780_common_home(struct charlcd *lcd)
67 {
68 	lcd->addr.x = 0;
69 	lcd->addr.y = 0;
70 	return hd44780_common_gotoxy(lcd);
71 }
72 EXPORT_SYMBOL_GPL(hd44780_common_home);
73 
74 /* clears the display and resets X/Y */
75 int hd44780_common_clear_display(struct charlcd *lcd)
76 {
77 	struct hd44780_common *hdc = lcd->drvdata;
78 
79 	hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CLEAR);
80 	/* we must wait a few milliseconds (15) */
81 	long_sleep(15);
82 	return 0;
83 }
84 EXPORT_SYMBOL_GPL(hd44780_common_clear_display);
85 
86 int hd44780_common_init_display(struct charlcd *lcd)
87 {
88 	struct hd44780_common *hdc = lcd->drvdata;
89 
90 	void (*write_cmd_raw)(struct hd44780_common *hdc, int cmd);
91 	u8 init;
92 
93 	if (hdc->ifwidth != 4 && hdc->ifwidth != 8)
94 		return -EINVAL;
95 
96 	hdc->hd44780_common_flags = ((lcd->height > 1) ? LCD_FLAG_N : 0) |
97 		LCD_FLAG_D | LCD_FLAG_C | LCD_FLAG_B;
98 
99 	long_sleep(20);		/* wait 20 ms after power-up for the paranoid */
100 
101 	/*
102 	 * 8-bit mode, 1 line, small fonts; let's do it 3 times, to make sure
103 	 * the LCD is in 8-bit mode afterwards
104 	 */
105 	init = LCD_CMD_FUNCTION_SET | LCD_CMD_DATA_LEN_8BITS;
106 	if (hdc->ifwidth == 4) {
107 		init >>= 4;
108 		write_cmd_raw = hdc->write_cmd_raw4;
109 	} else {
110 		write_cmd_raw = hdc->write_cmd;
111 	}
112 	write_cmd_raw(hdc, init);
113 	long_sleep(10);
114 	write_cmd_raw(hdc, init);
115 	long_sleep(10);
116 	write_cmd_raw(hdc, init);
117 	long_sleep(10);
118 
119 	if (hdc->ifwidth == 4) {
120 		/* Switch to 4-bit mode, 1 line, small fonts */
121 		hdc->write_cmd_raw4(hdc, LCD_CMD_FUNCTION_SET >> 4);
122 		long_sleep(10);
123 	}
124 
125 	/* set font height and lines number */
126 	hdc->write_cmd(hdc,
127 		LCD_CMD_FUNCTION_SET |
128 		((hdc->ifwidth == 8) ? LCD_CMD_DATA_LEN_8BITS : 0) |
129 		((hdc->hd44780_common_flags & LCD_FLAG_F) ?
130 			LCD_CMD_FONT_5X10_DOTS : 0) |
131 		((hdc->hd44780_common_flags & LCD_FLAG_N) ?
132 			LCD_CMD_TWO_LINES : 0));
133 	long_sleep(10);
134 
135 	/* display off, cursor off, blink off */
136 	hdc->write_cmd(hdc, LCD_CMD_DISPLAY_CTRL);
137 	long_sleep(10);
138 
139 	hdc->write_cmd(hdc,
140 		LCD_CMD_DISPLAY_CTRL |	/* set display mode */
141 		((hdc->hd44780_common_flags & LCD_FLAG_D) ?
142 			LCD_CMD_DISPLAY_ON : 0) |
143 		((hdc->hd44780_common_flags & LCD_FLAG_C) ?
144 			LCD_CMD_CURSOR_ON : 0) |
145 		((hdc->hd44780_common_flags & LCD_FLAG_B) ?
146 			LCD_CMD_BLINK_ON : 0));
147 
148 	charlcd_backlight(lcd,
149 			(hdc->hd44780_common_flags & LCD_FLAG_L) ? 1 : 0);
150 
151 	long_sleep(10);
152 
153 	/* entry mode set : increment, cursor shifting */
154 	hdc->write_cmd(hdc, LCD_CMD_ENTRY_MODE | LCD_CMD_CURSOR_INC);
155 
156 	hd44780_common_clear_display(lcd);
157 	return 0;
158 }
159 EXPORT_SYMBOL_GPL(hd44780_common_init_display);
160 
161 struct hd44780_common *hd44780_common_alloc(void)
162 {
163 	struct hd44780_common *hd;
164 
165 	hd = kzalloc(sizeof(*hd), GFP_KERNEL);
166 	if (!hd)
167 		return NULL;
168 
169 	hd->ifwidth = 8;
170 	hd->bwidth = DEFAULT_LCD_BWIDTH;
171 	hd->hwidth = DEFAULT_LCD_HWIDTH;
172 	return hd;
173 }
174 EXPORT_SYMBOL_GPL(hd44780_common_alloc);
175 
176 MODULE_LICENSE("GPL");
177