work_92105_display.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * work_92105 display support
  4. *
  5. * (C) Copyright 2014 DENX Software Engineering GmbH
  6. * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
  7. *
  8. * The work_92105 display is a HD44780-compatible module
  9. * controlled through a MAX6957AAX SPI port expander, two
  10. * MAX518 I2C DACs and native LPC32xx GPO 15.
  11. */
  12. #include <common.h>
  13. #include <command.h>
  14. #include <log.h>
  15. #include <asm/arch/sys_proto.h>
  16. #include <asm/arch/cpu.h>
  17. #include <asm/arch/emc.h>
  18. #include <asm/gpio.h>
  19. #include <env.h>
  20. #include <spi.h>
  21. #include <i2c.h>
  22. #include <version.h>
  23. #include <vsprintf.h>
  24. #include <linux/delay.h>
  25. /*
  26. * GPO 15 in port 3 is gpio 3*32+15 = 111
  27. */
  28. #define GPO_15 111
  29. /**
  30. * MAX6957AAX registers that we will be using
  31. */
  32. #define MAX6957_CONF 0x04
  33. #define MAX6957_CONF_08_11 0x0A
  34. #define MAX6957_CONF_12_15 0x0B
  35. #define MAX6957_CONF_16_19 0x0C
  36. /**
  37. * Individual gpio ports (one per gpio) to HD44780
  38. */
  39. #define MAX6957AAX_HD44780_RS 0x29
  40. #define MAX6957AAX_HD44780_R_W 0x2A
  41. #define MAX6957AAX_HD44780_EN 0x2B
  42. #define MAX6957AAX_HD44780_DATA 0x4C
  43. /**
  44. * Display controller instructions
  45. */
  46. /* Function set: eight bits, two lines, 8-dot font */
  47. #define HD44780_FUNCTION_SET 0x38
  48. /* Display ON / OFF: turn display on */
  49. #define HD44780_DISPLAY_ON_OFF_CONTROL 0x0C
  50. /* Entry mode: increment */
  51. #define HD44780_ENTRY_MODE_SET 0x06
  52. /* Clear */
  53. #define HD44780_CLEAR_DISPLAY 0x01
  54. /* Set DDRAM addr (to be ORed with exact address) */
  55. #define HD44780_SET_DDRAM_ADDR 0x80
  56. /* Set CGRAM addr (to be ORed with exact address) */
  57. #define HD44780_SET_CGRAM_ADDR 0x40
  58. /**
  59. * Default value for contrats
  60. */
  61. #define CONTRAST_DEFAULT 25
  62. /**
  63. * Define slave as a module-wide local to save passing it around,
  64. * plus we will need it after init for the "hd44780" command.
  65. */
  66. static struct spi_slave *slave;
  67. /*
  68. * Write a value into a MAX6957AAX register.
  69. */
  70. static void max6957aax_write(uint8_t reg, uint8_t value)
  71. {
  72. uint8_t dout[2];
  73. dout[0] = reg;
  74. dout[1] = value;
  75. gpio_set_value(GPO_15, 0);
  76. /* do SPI read/write (passing din==dout is OK) */
  77. spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
  78. gpio_set_value(GPO_15, 1);
  79. }
  80. /*
  81. * Read a value from a MAX6957AAX register.
  82. *
  83. * According to the MAX6957AAX datasheet, we should release the chip
  84. * select halfway through the read sequence, when the actual register
  85. * value is read; but the WORK_92105 hardware prevents the MAX6957AAX
  86. * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
  87. * so let's release the CS an hold it again while reading the result.
  88. */
  89. static uint8_t max6957aax_read(uint8_t reg)
  90. {
  91. uint8_t dout[2], din[2];
  92. /* send read command */
  93. dout[0] = reg | 0x80; /* set bit 7 to indicate read */
  94. dout[1] = 0;
  95. gpio_set_value(GPO_15, 0);
  96. /* do SPI read/write (passing din==dout is OK) */
  97. spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
  98. /* latch read command */
  99. gpio_set_value(GPO_15, 1);
  100. /* read register -- din = noop on xmit, din[1] = reg on recv */
  101. din[0] = 0;
  102. din[1] = 0;
  103. gpio_set_value(GPO_15, 0);
  104. /* do SPI read/write (passing din==dout is OK) */
  105. spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
  106. /* end of read. */
  107. gpio_set_value(GPO_15, 1);
  108. return din[1];
  109. }
  110. static void hd44780_instruction(unsigned long instruction)
  111. {
  112. max6957aax_write(MAX6957AAX_HD44780_RS, 0);
  113. max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
  114. max6957aax_write(MAX6957AAX_HD44780_EN, 1);
  115. max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
  116. max6957aax_write(MAX6957AAX_HD44780_EN, 0);
  117. /* HD44780 takes 37 us for most instructions, 1520 for clear */
  118. if (instruction == HD44780_CLEAR_DISPLAY)
  119. udelay(2000);
  120. else
  121. udelay(100);
  122. }
  123. static void hd44780_write_char(char c)
  124. {
  125. max6957aax_write(MAX6957AAX_HD44780_RS, 1);
  126. max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
  127. max6957aax_write(MAX6957AAX_HD44780_EN, 1);
  128. max6957aax_write(MAX6957AAX_HD44780_DATA, c);
  129. max6957aax_write(MAX6957AAX_HD44780_EN, 0);
  130. /* HD44780 takes 37 us to write to DDRAM or CGRAM */
  131. udelay(100);
  132. }
  133. static void hd44780_write_str(char *s)
  134. {
  135. max6957aax_write(MAX6957AAX_HD44780_RS, 1);
  136. max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
  137. while (*s) {
  138. max6957aax_write(MAX6957AAX_HD44780_EN, 1);
  139. max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
  140. max6957aax_write(MAX6957AAX_HD44780_EN, 0);
  141. s++;
  142. /* HD44780 takes 37 us to write to DDRAM or CGRAM */
  143. udelay(100);
  144. }
  145. }
  146. /*
  147. * Existing user code might expect these custom characters to be
  148. * recognized and displayed on the LCD
  149. */
  150. static u8 char_gen_chars[] = {
  151. /* #8, empty rectangle */
  152. 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
  153. /* #9, filled right arrow */
  154. 0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
  155. /* #10, filled left arrow */
  156. 0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
  157. /* #11, up and down arrow */
  158. 0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
  159. /* #12, plus/minus */
  160. 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
  161. /* #13, fat exclamation mark */
  162. 0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
  163. /* #14, empty square */
  164. 0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
  165. /* #15, struck out square */
  166. 0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
  167. };
  168. static void hd44780_init_char_gen(void)
  169. {
  170. int i;
  171. hd44780_instruction(HD44780_SET_CGRAM_ADDR);
  172. for (i = 0; i < sizeof(char_gen_chars); i++)
  173. hd44780_write_char(char_gen_chars[i]);
  174. hd44780_instruction(HD44780_SET_DDRAM_ADDR);
  175. }
  176. void work_92105_display_init(void)
  177. {
  178. int claim_err;
  179. char *display_contrast_str;
  180. uint8_t display_contrast = CONTRAST_DEFAULT;
  181. uint8_t enable_backlight = 0x96;
  182. slave = spi_setup_slave(0, 0, 500000, 0);
  183. if (!slave) {
  184. printf("Failed to set up SPI slave\n");
  185. return;
  186. }
  187. claim_err = spi_claim_bus(slave);
  188. if (claim_err)
  189. debug("Failed to claim SPI bus: %d\n", claim_err);
  190. /* enable backlight */
  191. i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);
  192. /* set display contrast */
  193. display_contrast_str = env_get("fwopt_dispcontrast");
  194. if (display_contrast_str)
  195. display_contrast = dectoul(display_contrast_str, NULL);
  196. i2c_write(0x2c, 0x00, 1, &display_contrast, 1);
  197. /* request GPO_15 as an output initially set to 1 */
  198. gpio_request(GPO_15, "MAX6957_nCS");
  199. gpio_direction_output(GPO_15, 1);
  200. /* enable MAX6957 portexpander */
  201. max6957aax_write(MAX6957_CONF, 0x01);
  202. /* configure pin 8 as input, pins 9..19 as outputs */
  203. max6957aax_write(MAX6957_CONF_08_11, 0x56);
  204. max6957aax_write(MAX6957_CONF_12_15, 0x55);
  205. max6957aax_write(MAX6957_CONF_16_19, 0x55);
  206. /* initialize HD44780 */
  207. max6957aax_write(MAX6957AAX_HD44780_EN, 0);
  208. hd44780_instruction(HD44780_FUNCTION_SET);
  209. hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL);
  210. hd44780_instruction(HD44780_ENTRY_MODE_SET);
  211. /* write custom character glyphs */
  212. hd44780_init_char_gen();
  213. /* Show U-Boot version, date and time as a sign-of-life */
  214. hd44780_instruction(HD44780_CLEAR_DISPLAY);
  215. hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0);
  216. hd44780_write_str(U_BOOT_VERSION);
  217. hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64);
  218. hd44780_write_str(U_BOOT_DATE);
  219. hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20);
  220. hd44780_write_str(U_BOOT_TIME);
  221. }
  222. #ifdef CONFIG_CMD_MAX6957
  223. static int do_max6957aax(struct cmd_tbl *cmdtp, int flag, int argc,
  224. char *const argv[])
  225. {
  226. int reg, val;
  227. if (argc != 3)
  228. return CMD_RET_USAGE;
  229. switch (argv[1][0]) {
  230. case 'r':
  231. case 'R':
  232. reg = simple_strtoul(argv[2], NULL, 0);
  233. val = max6957aax_read(reg);
  234. printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val);
  235. return 0;
  236. default:
  237. reg = simple_strtoul(argv[1], NULL, 0);
  238. val = simple_strtoul(argv[2], NULL, 0);
  239. max6957aax_write(reg, val);
  240. printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val);
  241. return 0;
  242. }
  243. return 1;
  244. }
  245. #ifdef CONFIG_SYS_LONGHELP
  246. static char max6957aax_help_text[] =
  247. "max6957aax - write or read display register:\n"
  248. "\tmax6957aax R|r reg - read display register;\n"
  249. "\tmax6957aax reg val - write display register.";
  250. #endif
  251. U_BOOT_CMD(
  252. max6957aax, 6, 1, do_max6957aax,
  253. "SPI MAX6957 display write/read",
  254. max6957aax_help_text
  255. );
  256. #endif /* CONFIG_CMD_MAX6957 */
  257. #ifdef CONFIG_CMD_HD44760
  258. /*
  259. * We need the HUSH parser because we need string arguments, and
  260. * only HUSH can understand them.
  261. */
  262. #if !defined(CONFIG_HUSH_PARSER)
  263. #error CONFIG_CMD_HD44760 requires CONFIG_HUSH_PARSER
  264. #endif
  265. static int do_hd44780(struct cmd_tbl *cmdtp, int flag, int argc,
  266. char *const argv[])
  267. {
  268. char *cmd;
  269. if (argc != 3)
  270. return CMD_RET_USAGE;
  271. cmd = argv[1];
  272. if (strcasecmp(cmd, "cmd") == 0)
  273. hd44780_instruction(simple_strtol(argv[2], NULL, 0));
  274. else if (strcasecmp(cmd, "data") == 0)
  275. hd44780_write_char(simple_strtol(argv[2], NULL, 0));
  276. else if (strcasecmp(cmd, "str") == 0)
  277. hd44780_write_str(argv[2]);
  278. return 0;
  279. }
  280. #ifdef CONFIG_SYS_LONGHELP
  281. static char hd44780_help_text[] =
  282. "hd44780 - control LCD driver:\n"
  283. "\thd44780 cmd <val> - send command <val> to driver;\n"
  284. "\thd44780 data <val> - send data <val> to driver;\n"
  285. "\thd44780 str \"<text>\" - send \"<text>\" to driver.";
  286. #endif
  287. U_BOOT_CMD(
  288. hd44780, 6, 1, do_hd44780,
  289. "HD44780 LCD driver control",
  290. hd44780_help_text
  291. );
  292. #endif /* CONFIG_CMD_HD44760 */