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