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