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