]>
Commit | Line | Data |
---|---|---|
412ae53a AA |
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 */ | |
00caae6d | 231 | display_contrast_str = env_get("fwopt_dispcontrast"); |
412ae53a AA |
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 | ||
f1f9d4fa MY |
314 | #if !defined(CONFIG_HUSH_PARSER) |
315 | #error CONFIG_CMD_HD44760 requires CONFIG_HUSH_PARSER | |
412ae53a AA |
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 | ); | |
e8e09ba5 | 349 | #endif /* CONFIG_CMD_HD44760 */ |