aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert@linux-m68k.org>2017-03-10 09:15:21 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-17 02:10:50 -0400
commitd47d88361feea2ce11f39bd70467ffc19a61d2d3 (patch)
tree0fe35cffc53cd9a2334ad6d517762978f80a05e5
parentdd9502a9e9156dd854dd0bec0dd385b4662bab3e (diff)
auxdisplay: Add HD44780 Character LCD support
The Hitachi HD44780 Character LCD Controller is commonly used on character LCDs that can display one or more lines of text. This driver supports character LCDs connected to GPIOs, using either a 4-bit or 8-bit data bus, and provides access through the charlcd core and /dev/lcd. Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/auxdisplay/Kconfig11
-rw-r--r--drivers/auxdisplay/Makefile1
-rw-r--r--drivers/auxdisplay/hd44780.c325
3 files changed, 337 insertions, 0 deletions
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
index b4686380d4e3..c317dc515617 100644
--- a/drivers/auxdisplay/Kconfig
+++ b/drivers/auxdisplay/Kconfig
@@ -18,6 +18,17 @@ config CHARLCD
18 18
19if AUXDISPLAY 19if AUXDISPLAY
20 20
21config HD44780
22 tristate "HD44780 Character LCD support"
23 depends on GPIOLIB || COMPILE_TEST
24 select CHARLCD
25 ---help---
26 Enable support for Character LCDs using a HD44780 controller.
27 The LCD is accessible through the /dev/lcd char device (10, 156).
28 This code can either be compiled as a module, or linked into the
29 kernel and started at boot.
30 If you don't understand what all this is about, say N.
31
21config KS0108 32config KS0108
22 tristate "KS0108 LCD Controller" 33 tristate "KS0108 LCD Controller"
23 depends on PARPORT_PC 34 depends on PARPORT_PC
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
index f56f45dcc78e..fcefa686be0f 100644
--- a/drivers/auxdisplay/Makefile
+++ b/drivers/auxdisplay/Makefile
@@ -6,4 +6,5 @@ obj-$(CONFIG_CHARLCD) += charlcd.o
6obj-$(CONFIG_KS0108) += ks0108.o 6obj-$(CONFIG_KS0108) += ks0108.o
7obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o 7obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
8obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o 8obj-$(CONFIG_IMG_ASCII_LCD) += img-ascii-lcd.o
9obj-$(CONFIG_HD44780) += hd44780.o
9obj-$(CONFIG_HT16K33) += ht16k33.o 10obj-$(CONFIG_HT16K33) += ht16k33.o
diff --git a/drivers/auxdisplay/hd44780.c b/drivers/auxdisplay/hd44780.c
new file mode 100644
index 000000000000..1665ac6ef9ff
--- /dev/null
+++ b/drivers/auxdisplay/hd44780.c
@@ -0,0 +1,325 @@
1/*
2 * HD44780 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/delay.h>
14#include <linux/gpio/consumer.h>
15#include <linux/module.h>
16#include <linux/platform_device.h>
17#include <linux/property.h>
18#include <linux/slab.h>
19
20#include <misc/charlcd.h>
21
22
23enum hd44780_pin {
24 /* Order does matter due to writing to GPIO array subsets! */
25 PIN_DATA0, /* Optional */
26 PIN_DATA1, /* Optional */
27 PIN_DATA2, /* Optional */
28 PIN_DATA3, /* Optional */
29 PIN_DATA4,
30 PIN_DATA5,
31 PIN_DATA6,
32 PIN_DATA7,
33 PIN_CTRL_RS,
34 PIN_CTRL_RW, /* Optional */
35 PIN_CTRL_E,
36 PIN_CTRL_BL, /* Optional */
37 PIN_NUM
38};
39
40struct hd44780 {
41 struct gpio_desc *pins[PIN_NUM];
42};
43
44static void hd44780_backlight(struct charlcd *lcd, int on)
45{
46 struct hd44780 *hd = lcd->drvdata;
47
48 if (hd->pins[PIN_CTRL_BL])
49 gpiod_set_value_cansleep(hd->pins[PIN_CTRL_BL], on);
50}
51
52static void hd44780_strobe_gpio(struct hd44780 *hd)
53{
54 /* Maintain the data during 20 us before the strobe */
55 udelay(20);
56
57 gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 1);
58
59 /* Maintain the strobe during 40 us */
60 udelay(40);
61
62 gpiod_set_value_cansleep(hd->pins[PIN_CTRL_E], 0);
63}
64
65/* write to an LCD panel register in 8 bit GPIO mode */
66static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs)
67{
68 int values[10]; /* for DATA[0-7], RS, RW */
69 unsigned int i, n;
70
71 for (i = 0; i < 8; i++)
72 values[PIN_DATA0 + i] = !!(val & BIT(i));
73 values[PIN_CTRL_RS] = rs;
74 n = 9;
75 if (hd->pins[PIN_CTRL_RW]) {
76 values[PIN_CTRL_RW] = 0;
77 n++;
78 }
79
80 /* Present the data to the port */
81 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA0], values);
82
83 hd44780_strobe_gpio(hd);
84}
85
86/* write to an LCD panel register in 4 bit GPIO mode */
87static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs)
88{
89 int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
90 unsigned int i, n;
91
92 /* High nibble + RS, RW */
93 for (i = 4; i < 8; i++)
94 values[PIN_DATA0 + i] = !!(val & BIT(i));
95 values[PIN_CTRL_RS] = rs;
96 n = 5;
97 if (hd->pins[PIN_CTRL_RW]) {
98 values[PIN_CTRL_RW] = 0;
99 n++;
100 }
101
102 /* Present the data to the port */
103 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
104 &values[PIN_DATA4]);
105
106 hd44780_strobe_gpio(hd);
107
108 /* Low nibble */
109 for (i = 0; i < 4; i++)
110 values[PIN_DATA4 + i] = !!(val & BIT(i));
111
112 /* Present the data to the port */
113 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
114 &values[PIN_DATA4]);
115
116 hd44780_strobe_gpio(hd);
117}
118
119/* Send a command to the LCD panel in 8 bit GPIO mode */
120static void hd44780_write_cmd_gpio8(struct charlcd *lcd, int cmd)
121{
122 struct hd44780 *hd = lcd->drvdata;
123
124 hd44780_write_gpio8(hd, cmd, 0);
125
126 /* The shortest command takes at least 120 us */
127 udelay(120);
128}
129
130/* Send data to the LCD panel in 8 bit GPIO mode */
131static void hd44780_write_data_gpio8(struct charlcd *lcd, int data)
132{
133 struct hd44780 *hd = lcd->drvdata;
134
135 hd44780_write_gpio8(hd, data, 1);
136
137 /* The shortest data takes at least 45 us */
138 udelay(45);
139}
140
141static const struct charlcd_ops hd44780_ops_gpio8 = {
142 .write_cmd = hd44780_write_cmd_gpio8,
143 .write_data = hd44780_write_data_gpio8,
144 .backlight = hd44780_backlight,
145};
146
147/* Send a command to the LCD panel in 4 bit GPIO mode */
148static void hd44780_write_cmd_gpio4(struct charlcd *lcd, int cmd)
149{
150 struct hd44780 *hd = lcd->drvdata;
151
152 hd44780_write_gpio4(hd, cmd, 0);
153
154 /* The shortest command takes at least 120 us */
155 udelay(120);
156}
157
158/* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */
159static void hd44780_write_cmd_raw_gpio4(struct charlcd *lcd, int cmd)
160{
161 int values[10]; /* for DATA[0-7], RS, RW, but DATA[0-3] is unused */
162 struct hd44780 *hd = lcd->drvdata;
163 unsigned int i, n;
164
165 /* Command nibble + RS, RW */
166 for (i = 0; i < 4; i++)
167 values[PIN_DATA4 + i] = !!(cmd & BIT(i));
168 values[PIN_CTRL_RS] = 0;
169 n = 5;
170 if (hd->pins[PIN_CTRL_RW]) {
171 values[PIN_CTRL_RW] = 0;
172 n++;
173 }
174
175 /* Present the data to the port */
176 gpiod_set_array_value_cansleep(n, &hd->pins[PIN_DATA4],
177 &values[PIN_DATA4]);
178
179 hd44780_strobe_gpio(hd);
180}
181
182/* Send data to the LCD panel in 4 bit GPIO mode */
183static void hd44780_write_data_gpio4(struct charlcd *lcd, int data)
184{
185 struct hd44780 *hd = lcd->drvdata;
186
187 hd44780_write_gpio4(hd, data, 1);
188
189 /* The shortest data takes at least 45 us */
190 udelay(45);
191}
192
193static const struct charlcd_ops hd44780_ops_gpio4 = {
194 .write_cmd = hd44780_write_cmd_gpio4,
195 .write_cmd_raw4 = hd44780_write_cmd_raw_gpio4,
196 .write_data = hd44780_write_data_gpio4,
197 .backlight = hd44780_backlight,
198};
199
200static int hd44780_probe(struct platform_device *pdev)
201{
202 struct device *dev = &pdev->dev;
203 unsigned int i, base;
204 struct charlcd *lcd;
205 struct hd44780 *hd;
206 int ifwidth, ret;
207
208 /* Required pins */
209 ifwidth = gpiod_count(dev, "data");
210 if (ifwidth < 0)
211 return ifwidth;
212
213 switch (ifwidth) {
214 case 4:
215 base = PIN_DATA4;
216 break;
217 case 8:
218 base = PIN_DATA0;
219 break;
220 default:
221 return -EINVAL;
222 }
223
224 lcd = charlcd_alloc(sizeof(struct hd44780));
225 if (!lcd)
226 return -ENOMEM;
227
228 hd = lcd->drvdata;
229
230 for (i = 0; i < ifwidth; i++) {
231 hd->pins[base + i] = devm_gpiod_get_index(dev, "data", i,
232 GPIOD_OUT_LOW);
233 if (IS_ERR(hd->pins[base + i])) {
234 ret = PTR_ERR(hd->pins[base + i]);
235 goto fail;
236 }
237 }
238
239 hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
240 if (IS_ERR(hd->pins[PIN_CTRL_E])) {
241 ret = PTR_ERR(hd->pins[PIN_CTRL_E]);
242 goto fail;
243 }
244
245 hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, "rs", GPIOD_OUT_HIGH);
246 if (IS_ERR(hd->pins[PIN_CTRL_RS])) {
247 ret = PTR_ERR(hd->pins[PIN_CTRL_RS]);
248 goto fail;
249 }
250
251 /* Optional pins */
252 hd->pins[PIN_CTRL_RW] = devm_gpiod_get_optional(dev, "rw",
253 GPIOD_OUT_LOW);
254 if (IS_ERR(hd->pins[PIN_CTRL_RW])) {
255 ret = PTR_ERR(hd->pins[PIN_CTRL_RW]);
256 goto fail;
257 }
258
259 hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, "backlight",
260 GPIOD_OUT_LOW);
261 if (IS_ERR(hd->pins[PIN_CTRL_BL])) {
262 ret = PTR_ERR(hd->pins[PIN_CTRL_BL]);
263 goto fail;
264 }
265
266 /* Required properties */
267 ret = device_property_read_u32(dev, "display-height", &lcd->height);
268 if (ret)
269 goto fail;
270 ret = device_property_read_u32(dev, "display-width", &lcd->width);
271 if (ret)
272 goto fail;
273
274 /*
275 * On displays with more than two rows, the internal buffer width is
276 * usually equal to the display width
277 */
278 if (lcd->height > 2)
279 lcd->bwidth = lcd->width;
280
281 /* Optional properties */
282 device_property_read_u32(dev, "internal-buffer-width", &lcd->bwidth);
283
284 lcd->ifwidth = ifwidth;
285 lcd->ops = ifwidth == 8 ? &hd44780_ops_gpio8 : &hd44780_ops_gpio4;
286
287 ret = charlcd_register(lcd);
288 if (ret)
289 goto fail;
290
291 platform_set_drvdata(pdev, lcd);
292 return 0;
293
294fail:
295 kfree(lcd);
296 return ret;
297}
298
299static int hd44780_remove(struct platform_device *pdev)
300{
301 struct charlcd *lcd = platform_get_drvdata(pdev);
302
303 charlcd_unregister(lcd);
304 return 0;
305}
306
307static const struct of_device_id hd44780_of_match[] = {
308 { .compatible = "hit,hd44780" },
309 { /* sentinel */ }
310};
311MODULE_DEVICE_TABLE(of, hd44780_of_match);
312
313static struct platform_driver hd44780_driver = {
314 .probe = hd44780_probe,
315 .remove = hd44780_remove,
316 .driver = {
317 .name = "hd44780",
318 .of_match_table = hd44780_of_match,
319 },
320};
321
322module_platform_driver(hd44780_driver);
323MODULE_DESCRIPTION("HD44780 Character LCD driver");
324MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
325MODULE_LICENSE("GPL");