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