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