PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/auxdisplay/hd44780.c

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