lcd2s.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Console driver for LCD2S 4x20 character displays connected through i2c.
  4. * The display also has a SPI interface, but the driver does not support
  5. * this yet.
  6. *
  7. * This is a driver allowing you to use a LCD2S 4x20 from Modtronix
  8. * engineering as auxdisplay character device.
  9. *
  10. * (C) 2019 by Lemonage Software GmbH
  11. * Author: Lars Pöschel <[email protected]>
  12. * All rights reserved.
  13. */
  14. #include <linux/kernel.h>
  15. #include <linux/mod_devicetable.h>
  16. #include <linux/module.h>
  17. #include <linux/property.h>
  18. #include <linux/slab.h>
  19. #include <linux/i2c.h>
  20. #include <linux/delay.h>
  21. #include "charlcd.h"
  22. #define LCD2S_CMD_CUR_MOVES_FWD 0x09
  23. #define LCD2S_CMD_CUR_BLINK_OFF 0x10
  24. #define LCD2S_CMD_CUR_UL_OFF 0x11
  25. #define LCD2S_CMD_DISPLAY_OFF 0x12
  26. #define LCD2S_CMD_CUR_BLINK_ON 0x18
  27. #define LCD2S_CMD_CUR_UL_ON 0x19
  28. #define LCD2S_CMD_DISPLAY_ON 0x1a
  29. #define LCD2S_CMD_BACKLIGHT_OFF 0x20
  30. #define LCD2S_CMD_BACKLIGHT_ON 0x28
  31. #define LCD2S_CMD_WRITE 0x80
  32. #define LCD2S_CMD_MOV_CUR_RIGHT 0x83
  33. #define LCD2S_CMD_MOV_CUR_LEFT 0x84
  34. #define LCD2S_CMD_SHIFT_RIGHT 0x85
  35. #define LCD2S_CMD_SHIFT_LEFT 0x86
  36. #define LCD2S_CMD_SHIFT_UP 0x87
  37. #define LCD2S_CMD_SHIFT_DOWN 0x88
  38. #define LCD2S_CMD_CUR_ADDR 0x89
  39. #define LCD2S_CMD_CUR_POS 0x8a
  40. #define LCD2S_CMD_CUR_RESET 0x8b
  41. #define LCD2S_CMD_CLEAR 0x8c
  42. #define LCD2S_CMD_DEF_CUSTOM_CHAR 0x92
  43. #define LCD2S_CMD_READ_STATUS 0xd0
  44. #define LCD2S_CHARACTER_SIZE 8
  45. #define LCD2S_STATUS_BUF_MASK 0x7f
  46. struct lcd2s_data {
  47. struct i2c_client *i2c;
  48. struct charlcd *charlcd;
  49. };
  50. static s32 lcd2s_wait_buf_free(const struct i2c_client *client, int count)
  51. {
  52. s32 status;
  53. status = i2c_smbus_read_byte_data(client, LCD2S_CMD_READ_STATUS);
  54. if (status < 0)
  55. return status;
  56. while ((status & LCD2S_STATUS_BUF_MASK) < count) {
  57. mdelay(1);
  58. status = i2c_smbus_read_byte_data(client,
  59. LCD2S_CMD_READ_STATUS);
  60. if (status < 0)
  61. return status;
  62. }
  63. return 0;
  64. }
  65. static int lcd2s_i2c_master_send(const struct i2c_client *client,
  66. const char *buf, int count)
  67. {
  68. s32 status;
  69. status = lcd2s_wait_buf_free(client, count);
  70. if (status < 0)
  71. return status;
  72. return i2c_master_send(client, buf, count);
  73. }
  74. static int lcd2s_i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
  75. {
  76. s32 status;
  77. status = lcd2s_wait_buf_free(client, 1);
  78. if (status < 0)
  79. return status;
  80. return i2c_smbus_write_byte(client, value);
  81. }
  82. static int lcd2s_print(struct charlcd *lcd, int c)
  83. {
  84. struct lcd2s_data *lcd2s = lcd->drvdata;
  85. u8 buf[2] = { LCD2S_CMD_WRITE, c };
  86. lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
  87. return 0;
  88. }
  89. static int lcd2s_gotoxy(struct charlcd *lcd, unsigned int x, unsigned int y)
  90. {
  91. struct lcd2s_data *lcd2s = lcd->drvdata;
  92. u8 buf[3] = { LCD2S_CMD_CUR_POS, y + 1, x + 1 };
  93. lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
  94. return 0;
  95. }
  96. static int lcd2s_home(struct charlcd *lcd)
  97. {
  98. struct lcd2s_data *lcd2s = lcd->drvdata;
  99. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_RESET);
  100. return 0;
  101. }
  102. static int lcd2s_init_display(struct charlcd *lcd)
  103. {
  104. struct lcd2s_data *lcd2s = lcd->drvdata;
  105. /* turn everything off, but display on */
  106. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
  107. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
  108. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_MOVES_FWD);
  109. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
  110. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
  111. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
  112. return 0;
  113. }
  114. static int lcd2s_shift_cursor(struct charlcd *lcd, enum charlcd_shift_dir dir)
  115. {
  116. struct lcd2s_data *lcd2s = lcd->drvdata;
  117. if (dir == CHARLCD_SHIFT_LEFT)
  118. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_LEFT);
  119. else
  120. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_MOV_CUR_RIGHT);
  121. return 0;
  122. }
  123. static int lcd2s_shift_display(struct charlcd *lcd, enum charlcd_shift_dir dir)
  124. {
  125. struct lcd2s_data *lcd2s = lcd->drvdata;
  126. if (dir == CHARLCD_SHIFT_LEFT)
  127. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_LEFT);
  128. else
  129. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_SHIFT_RIGHT);
  130. return 0;
  131. }
  132. static void lcd2s_backlight(struct charlcd *lcd, enum charlcd_onoff on)
  133. {
  134. struct lcd2s_data *lcd2s = lcd->drvdata;
  135. if (on)
  136. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_ON);
  137. else
  138. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_BACKLIGHT_OFF);
  139. }
  140. static int lcd2s_display(struct charlcd *lcd, enum charlcd_onoff on)
  141. {
  142. struct lcd2s_data *lcd2s = lcd->drvdata;
  143. if (on)
  144. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_ON);
  145. else
  146. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_DISPLAY_OFF);
  147. return 0;
  148. }
  149. static int lcd2s_cursor(struct charlcd *lcd, enum charlcd_onoff on)
  150. {
  151. struct lcd2s_data *lcd2s = lcd->drvdata;
  152. if (on)
  153. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_ON);
  154. else
  155. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_UL_OFF);
  156. return 0;
  157. }
  158. static int lcd2s_blink(struct charlcd *lcd, enum charlcd_onoff on)
  159. {
  160. struct lcd2s_data *lcd2s = lcd->drvdata;
  161. if (on)
  162. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_ON);
  163. else
  164. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CUR_BLINK_OFF);
  165. return 0;
  166. }
  167. static int lcd2s_fontsize(struct charlcd *lcd, enum charlcd_fontsize size)
  168. {
  169. return 0;
  170. }
  171. static int lcd2s_lines(struct charlcd *lcd, enum charlcd_lines lines)
  172. {
  173. return 0;
  174. }
  175. /*
  176. * Generator: LGcxxxxx...xx; must have <c> between '0' and '7',
  177. * representing the numerical ASCII code of the redefined character,
  178. * and <xx...xx> a sequence of 16 hex digits representing 8 bytes
  179. * for each character. Most LCDs will only use 5 lower bits of
  180. * the 7 first bytes.
  181. */
  182. static int lcd2s_redefine_char(struct charlcd *lcd, char *esc)
  183. {
  184. struct lcd2s_data *lcd2s = lcd->drvdata;
  185. u8 buf[LCD2S_CHARACTER_SIZE + 2] = { LCD2S_CMD_DEF_CUSTOM_CHAR };
  186. u8 value;
  187. int shift, i;
  188. if (!strchr(esc, ';'))
  189. return 0;
  190. esc++;
  191. buf[1] = *(esc++) - '0';
  192. if (buf[1] > 7)
  193. return 1;
  194. i = 2;
  195. shift = 0;
  196. value = 0;
  197. while (*esc && i < LCD2S_CHARACTER_SIZE + 2) {
  198. int half;
  199. shift ^= 4;
  200. half = hex_to_bin(*esc++);
  201. if (half < 0)
  202. continue;
  203. value |= half << shift;
  204. if (shift == 0) {
  205. buf[i++] = value;
  206. value = 0;
  207. }
  208. }
  209. lcd2s_i2c_master_send(lcd2s->i2c, buf, sizeof(buf));
  210. return 1;
  211. }
  212. static int lcd2s_clear_display(struct charlcd *lcd)
  213. {
  214. struct lcd2s_data *lcd2s = lcd->drvdata;
  215. /* This implicitly sets cursor to first row and column */
  216. lcd2s_i2c_smbus_write_byte(lcd2s->i2c, LCD2S_CMD_CLEAR);
  217. return 0;
  218. }
  219. static const struct charlcd_ops lcd2s_ops = {
  220. .print = lcd2s_print,
  221. .backlight = lcd2s_backlight,
  222. .gotoxy = lcd2s_gotoxy,
  223. .home = lcd2s_home,
  224. .clear_display = lcd2s_clear_display,
  225. .init_display = lcd2s_init_display,
  226. .shift_cursor = lcd2s_shift_cursor,
  227. .shift_display = lcd2s_shift_display,
  228. .display = lcd2s_display,
  229. .cursor = lcd2s_cursor,
  230. .blink = lcd2s_blink,
  231. .fontsize = lcd2s_fontsize,
  232. .lines = lcd2s_lines,
  233. .redefine_char = lcd2s_redefine_char,
  234. };
  235. static int lcd2s_i2c_probe(struct i2c_client *i2c)
  236. {
  237. struct charlcd *lcd;
  238. struct lcd2s_data *lcd2s;
  239. int err;
  240. if (!i2c_check_functionality(i2c->adapter,
  241. I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
  242. I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
  243. return -EIO;
  244. lcd2s = devm_kzalloc(&i2c->dev, sizeof(*lcd2s), GFP_KERNEL);
  245. if (!lcd2s)
  246. return -ENOMEM;
  247. /* Test, if the display is responding */
  248. err = lcd2s_i2c_smbus_write_byte(i2c, LCD2S_CMD_DISPLAY_OFF);
  249. if (err < 0)
  250. return err;
  251. lcd = charlcd_alloc();
  252. if (!lcd)
  253. return -ENOMEM;
  254. lcd->drvdata = lcd2s;
  255. lcd2s->i2c = i2c;
  256. lcd2s->charlcd = lcd;
  257. /* Required properties */
  258. err = device_property_read_u32(&i2c->dev, "display-height-chars",
  259. &lcd->height);
  260. if (err)
  261. goto fail1;
  262. err = device_property_read_u32(&i2c->dev, "display-width-chars",
  263. &lcd->width);
  264. if (err)
  265. goto fail1;
  266. lcd->ops = &lcd2s_ops;
  267. err = charlcd_register(lcd2s->charlcd);
  268. if (err)
  269. goto fail1;
  270. i2c_set_clientdata(i2c, lcd2s);
  271. return 0;
  272. fail1:
  273. charlcd_free(lcd2s->charlcd);
  274. return err;
  275. }
  276. static void lcd2s_i2c_remove(struct i2c_client *i2c)
  277. {
  278. struct lcd2s_data *lcd2s = i2c_get_clientdata(i2c);
  279. charlcd_unregister(lcd2s->charlcd);
  280. charlcd_free(lcd2s->charlcd);
  281. }
  282. static const struct i2c_device_id lcd2s_i2c_id[] = {
  283. { "lcd2s", 0 },
  284. { }
  285. };
  286. MODULE_DEVICE_TABLE(i2c, lcd2s_i2c_id);
  287. static const struct of_device_id lcd2s_of_table[] = {
  288. { .compatible = "modtronix,lcd2s" },
  289. { }
  290. };
  291. MODULE_DEVICE_TABLE(of, lcd2s_of_table);
  292. static struct i2c_driver lcd2s_i2c_driver = {
  293. .driver = {
  294. .name = "lcd2s",
  295. .of_match_table = lcd2s_of_table,
  296. },
  297. .probe_new = lcd2s_i2c_probe,
  298. .remove = lcd2s_i2c_remove,
  299. .id_table = lcd2s_i2c_id,
  300. };
  301. module_i2c_driver(lcd2s_i2c_driver);
  302. MODULE_DESCRIPTION("LCD2S character display driver");
  303. MODULE_AUTHOR("Lars Poeschel");
  304. MODULE_LICENSE("GPL");