common: Drop linux/delay.h from common header
[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 #include <linux/delay.h>
26
27 /*
28  * GPO 15 in port 3 is gpio 3*32+15 = 111
29  */
30
31 #define GPO_15 111
32
33 /**
34  * MAX6957AAX registers that we will be using
35  */
36
37 #define MAX6957_CONF            0x04
38
39 #define MAX6957_CONF_08_11      0x0A
40 #define MAX6957_CONF_12_15      0x0B
41 #define MAX6957_CONF_16_19      0x0C
42
43 /**
44  * Individual gpio ports (one per gpio) to HD44780
45  */
46
47 #define MAX6957AAX_HD44780_RS   0x29
48 #define MAX6957AAX_HD44780_R_W  0x2A
49 #define MAX6957AAX_HD44780_EN   0x2B
50 #define MAX6957AAX_HD44780_DATA 0x4C
51
52 /**
53  * Display controller instructions
54  */
55
56 /* Function set: eight bits, two lines, 8-dot font */
57 #define HD44780_FUNCTION_SET            0x38
58
59 /* Display ON / OFF: turn display on */
60 #define HD44780_DISPLAY_ON_OFF_CONTROL  0x0C
61
62 /* Entry mode: increment */
63 #define HD44780_ENTRY_MODE_SET          0x06
64
65 /* Clear */
66 #define HD44780_CLEAR_DISPLAY           0x01
67
68 /* Set DDRAM addr (to be ORed with exact address) */
69 #define HD44780_SET_DDRAM_ADDR          0x80
70
71 /* Set CGRAM addr (to be ORed with exact address) */
72 #define HD44780_SET_CGRAM_ADDR          0x40
73
74 /**
75  * Default value for contrats
76  */
77
78 #define CONTRAST_DEFAULT  25
79
80 /**
81  * Define slave as a module-wide local to save passing it around,
82  * plus we will need it after init for the "hd44780" command.
83  */
84
85 static struct spi_slave *slave;
86
87 /*
88  * Write a value into a MAX6957AAX register.
89  */
90
91 static void max6957aax_write(uint8_t reg, uint8_t value)
92 {
93         uint8_t dout[2];
94
95         dout[0] = reg;
96         dout[1] = value;
97         gpio_set_value(GPO_15, 0);
98         /* do SPI read/write (passing din==dout is OK) */
99         spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
100         gpio_set_value(GPO_15, 1);
101 }
102
103 /*
104  * Read a value from a MAX6957AAX register.
105  *
106  * According to the MAX6957AAX datasheet, we should release the chip
107  * select halfway through the read sequence, when the actual register
108  * value is read; but the WORK_92105 hardware prevents the MAX6957AAX
109  * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
110  * so let's release the CS an hold it again while reading the result.
111  */
112
113 static uint8_t max6957aax_read(uint8_t reg)
114 {
115         uint8_t dout[2], din[2];
116
117         /* send read command */
118         dout[0] = reg | 0x80; /* set bit 7 to indicate read */
119         dout[1] = 0;
120         gpio_set_value(GPO_15, 0);
121         /* do SPI read/write (passing din==dout is OK) */
122         spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
123         /* latch read command */
124         gpio_set_value(GPO_15, 1);
125         /* read register -- din = noop on xmit, din[1] = reg on recv */
126         din[0] = 0;
127         din[1] = 0;
128         gpio_set_value(GPO_15, 0);
129         /* do SPI read/write (passing din==dout is OK) */
130         spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
131         /* end of read. */
132         gpio_set_value(GPO_15, 1);
133         return din[1];
134 }
135
136 static void hd44780_instruction(unsigned long instruction)
137 {
138         max6957aax_write(MAX6957AAX_HD44780_RS, 0);
139         max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
140         max6957aax_write(MAX6957AAX_HD44780_EN, 1);
141         max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
142         max6957aax_write(MAX6957AAX_HD44780_EN, 0);
143         /* HD44780 takes 37 us for most instructions, 1520 for clear */
144         if (instruction == HD44780_CLEAR_DISPLAY)
145                 udelay(2000);
146         else
147                 udelay(100);
148 }
149
150 static void hd44780_write_char(char c)
151 {
152         max6957aax_write(MAX6957AAX_HD44780_RS, 1);
153         max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
154         max6957aax_write(MAX6957AAX_HD44780_EN, 1);
155         max6957aax_write(MAX6957AAX_HD44780_DATA, c);
156         max6957aax_write(MAX6957AAX_HD44780_EN, 0);
157         /* HD44780 takes 37 us to write to DDRAM or CGRAM */
158         udelay(100);
159 }
160
161 static void hd44780_write_str(char *s)
162 {
163         max6957aax_write(MAX6957AAX_HD44780_RS, 1);
164         max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
165         while (*s) {
166                 max6957aax_write(MAX6957AAX_HD44780_EN, 1);
167                 max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
168                 max6957aax_write(MAX6957AAX_HD44780_EN, 0);
169                 s++;
170                 /* HD44780 takes 37 us to write to DDRAM or CGRAM */
171                 udelay(100);
172         }
173 }
174
175 /*
176  * Existing user code might expect these custom characters to be
177  * recognized and displayed on the LCD
178  */
179
180 static u8 char_gen_chars[] = {
181         /* #8, empty rectangle */
182         0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
183         /* #9, filled right arrow */
184         0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
185         /* #10, filled left arrow */
186         0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
187         /* #11, up and down arrow */
188         0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
189         /* #12, plus/minus */
190         0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
191         /* #13, fat exclamation mark */
192         0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
193         /* #14, empty square */
194         0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
195         /* #15, struck out square */
196         0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
197 };
198
199 static void hd44780_init_char_gen(void)
200 {
201         int i;
202
203         hd44780_instruction(HD44780_SET_CGRAM_ADDR);
204
205         for (i = 0; i < sizeof(char_gen_chars); i++)
206                 hd44780_write_char(char_gen_chars[i]);
207
208         hd44780_instruction(HD44780_SET_DDRAM_ADDR);
209 }
210
211 void work_92105_display_init(void)
212 {
213         int claim_err;
214         char *display_contrast_str;
215         uint8_t display_contrast = CONTRAST_DEFAULT;
216         uint8_t enable_backlight = 0x96;
217
218         slave = spi_setup_slave(0, 0, 500000, 0);
219
220         if (!slave) {
221                 printf("Failed to set up SPI slave\n");
222                 return;
223         }
224
225         claim_err = spi_claim_bus(slave);
226
227         if (claim_err)
228                 debug("Failed to claim SPI bus: %d\n", claim_err);
229
230         /* enable backlight */
231         i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);
232
233         /* set display contrast */
234         display_contrast_str = env_get("fwopt_dispcontrast");
235         if (display_contrast_str)
236                 display_contrast = simple_strtoul(display_contrast_str,
237                         NULL, 10);
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
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
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 */