1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * HD44780 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/delay.h> |
10 | #include <linux/gpio/consumer.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/property.h> |
15 | #include <linux/slab.h> |
16 | |
17 | #include "charlcd.h" |
18 | #include "hd44780_common.h" |
19 | |
20 | enum hd44780_pin { |
21 | /* Order does matter due to writing to GPIO array subsets! */ |
22 | PIN_DATA0, /* Optional */ |
23 | PIN_DATA1, /* Optional */ |
24 | PIN_DATA2, /* Optional */ |
25 | PIN_DATA3, /* Optional */ |
26 | PIN_DATA4, |
27 | PIN_DATA5, |
28 | PIN_DATA6, |
29 | PIN_DATA7, |
30 | PIN_CTRL_RS, |
31 | PIN_CTRL_RW, /* Optional */ |
32 | PIN_CTRL_E, |
33 | PIN_CTRL_BL, /* Optional */ |
34 | PIN_NUM |
35 | }; |
36 | |
37 | struct hd44780 { |
38 | struct gpio_desc *pins[PIN_NUM]; |
39 | }; |
40 | |
41 | static void hd44780_backlight(struct charlcd *lcd, enum charlcd_onoff on) |
42 | { |
43 | struct hd44780_common *hdc = lcd->drvdata; |
44 | struct hd44780 *hd = hdc->hd44780; |
45 | |
46 | if (hd->pins[PIN_CTRL_BL]) |
47 | gpiod_set_value_cansleep(desc: hd->pins[PIN_CTRL_BL], value: on); |
48 | } |
49 | |
50 | static void hd44780_strobe_gpio(struct hd44780 *hd) |
51 | { |
52 | /* Maintain the data during 20 us before the strobe */ |
53 | udelay(20); |
54 | |
55 | gpiod_set_value_cansleep(desc: hd->pins[PIN_CTRL_E], value: 1); |
56 | |
57 | /* Maintain the strobe during 40 us */ |
58 | udelay(40); |
59 | |
60 | gpiod_set_value_cansleep(desc: hd->pins[PIN_CTRL_E], value: 0); |
61 | } |
62 | |
63 | /* write to an LCD panel register in 8 bit GPIO mode */ |
64 | static void hd44780_write_gpio8(struct hd44780 *hd, u8 val, unsigned int rs) |
65 | { |
66 | DECLARE_BITMAP(values, 10); /* for DATA[0-7], RS, RW */ |
67 | unsigned int n; |
68 | |
69 | values[0] = val; |
70 | __assign_bit(nr: 8, addr: values, value: rs); |
71 | n = hd->pins[PIN_CTRL_RW] ? 10 : 9; |
72 | |
73 | /* Present the data to the port */ |
74 | gpiod_set_array_value_cansleep(array_size: n, desc_array: &hd->pins[PIN_DATA0], NULL, value_bitmap: values); |
75 | |
76 | hd44780_strobe_gpio(hd); |
77 | } |
78 | |
79 | /* write to an LCD panel register in 4 bit GPIO mode */ |
80 | static void hd44780_write_gpio4(struct hd44780 *hd, u8 val, unsigned int rs) |
81 | { |
82 | DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */ |
83 | unsigned int n; |
84 | |
85 | /* High nibble + RS, RW */ |
86 | values[0] = val >> 4; |
87 | __assign_bit(nr: 4, addr: values, value: rs); |
88 | n = hd->pins[PIN_CTRL_RW] ? 6 : 5; |
89 | |
90 | /* Present the data to the port */ |
91 | gpiod_set_array_value_cansleep(array_size: n, desc_array: &hd->pins[PIN_DATA4], NULL, value_bitmap: values); |
92 | |
93 | hd44780_strobe_gpio(hd); |
94 | |
95 | /* Low nibble */ |
96 | values[0] &= ~0x0fUL; |
97 | values[0] |= val & 0x0f; |
98 | |
99 | /* Present the data to the port */ |
100 | gpiod_set_array_value_cansleep(array_size: n, desc_array: &hd->pins[PIN_DATA4], NULL, value_bitmap: values); |
101 | |
102 | hd44780_strobe_gpio(hd); |
103 | } |
104 | |
105 | /* Send a command to the LCD panel in 8 bit GPIO mode */ |
106 | static void hd44780_write_cmd_gpio8(struct hd44780_common *hdc, int cmd) |
107 | { |
108 | struct hd44780 *hd = hdc->hd44780; |
109 | |
110 | hd44780_write_gpio8(hd, val: cmd, rs: 0); |
111 | |
112 | /* The shortest command takes at least 120 us */ |
113 | udelay(120); |
114 | } |
115 | |
116 | /* Send data to the LCD panel in 8 bit GPIO mode */ |
117 | static void hd44780_write_data_gpio8(struct hd44780_common *hdc, int data) |
118 | { |
119 | struct hd44780 *hd = hdc->hd44780; |
120 | |
121 | hd44780_write_gpio8(hd, val: data, rs: 1); |
122 | |
123 | /* The shortest data takes at least 45 us */ |
124 | udelay(45); |
125 | } |
126 | |
127 | static const struct charlcd_ops hd44780_ops_gpio8 = { |
128 | .backlight = hd44780_backlight, |
129 | .print = hd44780_common_print, |
130 | .gotoxy = hd44780_common_gotoxy, |
131 | .home = hd44780_common_home, |
132 | .clear_display = hd44780_common_clear_display, |
133 | .init_display = hd44780_common_init_display, |
134 | .shift_cursor = hd44780_common_shift_cursor, |
135 | .shift_display = hd44780_common_shift_display, |
136 | .display = hd44780_common_display, |
137 | .cursor = hd44780_common_cursor, |
138 | .blink = hd44780_common_blink, |
139 | .fontsize = hd44780_common_fontsize, |
140 | .lines = hd44780_common_lines, |
141 | .redefine_char = hd44780_common_redefine_char, |
142 | }; |
143 | |
144 | /* Send a command to the LCD panel in 4 bit GPIO mode */ |
145 | static void hd44780_write_cmd_gpio4(struct hd44780_common *hdc, int cmd) |
146 | { |
147 | struct hd44780 *hd = hdc->hd44780; |
148 | |
149 | hd44780_write_gpio4(hd, val: cmd, rs: 0); |
150 | |
151 | /* The shortest command takes at least 120 us */ |
152 | udelay(120); |
153 | } |
154 | |
155 | /* Send 4-bits of a command to the LCD panel in raw 4 bit GPIO mode */ |
156 | static void hd44780_write_cmd_raw_gpio4(struct hd44780_common *hdc, int cmd) |
157 | { |
158 | DECLARE_BITMAP(values, 6); /* for DATA[4-7], RS, RW */ |
159 | struct hd44780 *hd = hdc->hd44780; |
160 | unsigned int n; |
161 | |
162 | /* Command nibble + RS, RW */ |
163 | values[0] = cmd & 0x0f; |
164 | n = hd->pins[PIN_CTRL_RW] ? 6 : 5; |
165 | |
166 | /* Present the data to the port */ |
167 | gpiod_set_array_value_cansleep(array_size: n, desc_array: &hd->pins[PIN_DATA4], NULL, value_bitmap: values); |
168 | |
169 | hd44780_strobe_gpio(hd); |
170 | } |
171 | |
172 | /* Send data to the LCD panel in 4 bit GPIO mode */ |
173 | static void hd44780_write_data_gpio4(struct hd44780_common *hdc, int data) |
174 | { |
175 | struct hd44780 *hd = hdc->hd44780; |
176 | |
177 | hd44780_write_gpio4(hd, val: data, rs: 1); |
178 | |
179 | /* The shortest data takes at least 45 us */ |
180 | udelay(45); |
181 | } |
182 | |
183 | static const struct charlcd_ops hd44780_ops_gpio4 = { |
184 | .backlight = hd44780_backlight, |
185 | .print = hd44780_common_print, |
186 | .gotoxy = hd44780_common_gotoxy, |
187 | .home = hd44780_common_home, |
188 | .clear_display = hd44780_common_clear_display, |
189 | .init_display = hd44780_common_init_display, |
190 | .shift_cursor = hd44780_common_shift_cursor, |
191 | .shift_display = hd44780_common_shift_display, |
192 | .display = hd44780_common_display, |
193 | .cursor = hd44780_common_cursor, |
194 | .blink = hd44780_common_blink, |
195 | .fontsize = hd44780_common_fontsize, |
196 | .lines = hd44780_common_lines, |
197 | .redefine_char = hd44780_common_redefine_char, |
198 | }; |
199 | |
200 | static 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_common *hdc; |
206 | struct hd44780 *hd; |
207 | int ifwidth, ret = -ENOMEM; |
208 | |
209 | /* Required pins */ |
210 | ifwidth = gpiod_count(dev, con_id: "data" ); |
211 | if (ifwidth < 0) |
212 | return ifwidth; |
213 | |
214 | switch (ifwidth) { |
215 | case 4: |
216 | base = PIN_DATA4; |
217 | break; |
218 | case 8: |
219 | base = PIN_DATA0; |
220 | break; |
221 | default: |
222 | return -EINVAL; |
223 | } |
224 | |
225 | hdc = hd44780_common_alloc(); |
226 | if (!hdc) |
227 | return -ENOMEM; |
228 | |
229 | lcd = charlcd_alloc(); |
230 | if (!lcd) |
231 | goto fail1; |
232 | |
233 | hd = kzalloc(size: sizeof(struct hd44780), GFP_KERNEL); |
234 | if (!hd) |
235 | goto fail2; |
236 | |
237 | hdc->hd44780 = hd; |
238 | lcd->drvdata = hdc; |
239 | for (i = 0; i < ifwidth; i++) { |
240 | hd->pins[base + i] = devm_gpiod_get_index(dev, con_id: "data" , idx: i, |
241 | flags: GPIOD_OUT_LOW); |
242 | if (IS_ERR(ptr: hd->pins[base + i])) { |
243 | ret = PTR_ERR(ptr: hd->pins[base + i]); |
244 | goto fail3; |
245 | } |
246 | } |
247 | |
248 | hd->pins[PIN_CTRL_E] = devm_gpiod_get(dev, con_id: "enable" , flags: GPIOD_OUT_LOW); |
249 | if (IS_ERR(ptr: hd->pins[PIN_CTRL_E])) { |
250 | ret = PTR_ERR(ptr: hd->pins[PIN_CTRL_E]); |
251 | goto fail3; |
252 | } |
253 | |
254 | hd->pins[PIN_CTRL_RS] = devm_gpiod_get(dev, con_id: "rs" , flags: GPIOD_OUT_HIGH); |
255 | if (IS_ERR(ptr: hd->pins[PIN_CTRL_RS])) { |
256 | ret = PTR_ERR(ptr: hd->pins[PIN_CTRL_RS]); |
257 | goto fail3; |
258 | } |
259 | |
260 | /* Optional pins */ |
261 | hd->pins[PIN_CTRL_RW] = devm_gpiod_get_optional(dev, con_id: "rw" , |
262 | flags: GPIOD_OUT_LOW); |
263 | if (IS_ERR(ptr: hd->pins[PIN_CTRL_RW])) { |
264 | ret = PTR_ERR(ptr: hd->pins[PIN_CTRL_RW]); |
265 | goto fail3; |
266 | } |
267 | |
268 | hd->pins[PIN_CTRL_BL] = devm_gpiod_get_optional(dev, con_id: "backlight" , |
269 | flags: GPIOD_OUT_LOW); |
270 | if (IS_ERR(ptr: hd->pins[PIN_CTRL_BL])) { |
271 | ret = PTR_ERR(ptr: hd->pins[PIN_CTRL_BL]); |
272 | goto fail3; |
273 | } |
274 | |
275 | /* Required properties */ |
276 | ret = device_property_read_u32(dev, propname: "display-height-chars" , |
277 | val: &lcd->height); |
278 | if (ret) |
279 | goto fail3; |
280 | ret = device_property_read_u32(dev, propname: "display-width-chars" , val: &lcd->width); |
281 | if (ret) |
282 | goto fail3; |
283 | |
284 | /* |
285 | * On displays with more than two rows, the internal buffer width is |
286 | * usually equal to the display width |
287 | */ |
288 | if (lcd->height > 2) |
289 | hdc->bwidth = lcd->width; |
290 | |
291 | /* Optional properties */ |
292 | device_property_read_u32(dev, propname: "internal-buffer-width" , val: &hdc->bwidth); |
293 | |
294 | hdc->ifwidth = ifwidth; |
295 | if (ifwidth == 8) { |
296 | lcd->ops = &hd44780_ops_gpio8; |
297 | hdc->write_data = hd44780_write_data_gpio8; |
298 | hdc->write_cmd = hd44780_write_cmd_gpio8; |
299 | } else { |
300 | lcd->ops = &hd44780_ops_gpio4; |
301 | hdc->write_data = hd44780_write_data_gpio4; |
302 | hdc->write_cmd = hd44780_write_cmd_gpio4; |
303 | hdc->write_cmd_raw4 = hd44780_write_cmd_raw_gpio4; |
304 | } |
305 | |
306 | ret = charlcd_register(lcd); |
307 | if (ret) |
308 | goto fail3; |
309 | |
310 | platform_set_drvdata(pdev, data: lcd); |
311 | return 0; |
312 | |
313 | fail3: |
314 | kfree(objp: hd); |
315 | fail2: |
316 | kfree(objp: lcd); |
317 | fail1: |
318 | kfree(objp: hdc); |
319 | return ret; |
320 | } |
321 | |
322 | static void hd44780_remove(struct platform_device *pdev) |
323 | { |
324 | struct charlcd *lcd = platform_get_drvdata(pdev); |
325 | struct hd44780_common *hdc = lcd->drvdata; |
326 | |
327 | charlcd_unregister(lcd); |
328 | kfree(objp: hdc->hd44780); |
329 | kfree(objp: lcd->drvdata); |
330 | |
331 | kfree(objp: lcd); |
332 | } |
333 | |
334 | static const struct of_device_id hd44780_of_match[] = { |
335 | { .compatible = "hit,hd44780" }, |
336 | { /* sentinel */ } |
337 | }; |
338 | MODULE_DEVICE_TABLE(of, hd44780_of_match); |
339 | |
340 | static struct platform_driver hd44780_driver = { |
341 | .probe = hd44780_probe, |
342 | .remove_new = hd44780_remove, |
343 | .driver = { |
344 | .name = "hd44780" , |
345 | .of_match_table = hd44780_of_match, |
346 | }, |
347 | }; |
348 | |
349 | module_platform_driver(hd44780_driver); |
350 | MODULE_DESCRIPTION("HD44780 Character LCD driver" ); |
351 | MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>" ); |
352 | MODULE_LICENSE("GPL" ); |
353 | |