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