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 <asm/arch/sys_proto.h>
15 #include <asm/arch/cpu.h>
16 #include <asm/arch/emc.h>
17 #include <asm/gpio.h>
18 #include <spi.h>
19 #include <i2c.h>
20 #include <timestamp.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 
max6957aax_write(uint8_t reg,uint8_t value)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 
max6957aax_read(uint8_t reg)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 
hd44780_instruction(unsigned long instruction)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 
hd44780_write_char(char c)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 
hd44780_write_str(char * s)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 
hd44780_init_char_gen(void)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 
work_92105_display_init(void)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 = env_get("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 
do_max6957aax(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])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_HUSH_PARSER)
315 #error CONFIG_CMD_HD44760 requires CONFIG_HUSH_PARSER
316 #endif
317 
do_hd44780(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])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_HD44760 */
350