summaryrefslogtreecommitdiffstats
path: root/board/work-microwave/work_92105/work_92105_display.c
blob: 3d7438e5277e5ff99b6ce8aa0d8a6fc0522b628c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
 * work_92105 display support
 *
 * (C) Copyright 2014  DENX Software Engineering GmbH
 * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
 *
 * The work_92105 display is a HD44780-compatible module
 * controlled through a MAX6957AAX SPI port expander, two
 * MAX518 I2C DACs and native LPC32xx GPO 15.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch/cpu.h>
#include <asm/arch/emc.h>
#include <asm/gpio.h>
#include <spi.h>
#include <i2c.h>
#include <version.h>
#include <vsprintf.h>

/*
 * GPO 15 in port 3 is gpio 3*32+15 = 111
 */

#define GPO_15 111

/**
 * MAX6957AAX registers that we will be using
 */

#define MAX6957_CONF		0x04

#define MAX6957_CONF_08_11	0x0A
#define MAX6957_CONF_12_15	0x0B
#define MAX6957_CONF_16_19	0x0C

/**
 * Individual gpio ports (one per gpio) to HD44780
 */

#define MAX6957AAX_HD44780_RS	0x29
#define MAX6957AAX_HD44780_R_W	0x2A
#define MAX6957AAX_HD44780_EN	0x2B
#define MAX6957AAX_HD44780_DATA	0x4C

/**
 * Display controller instructions
 */

/* Function set: eight bits, two lines, 8-dot font */
#define HD44780_FUNCTION_SET		0x38

/* Display ON / OFF: turn display on */
#define HD44780_DISPLAY_ON_OFF_CONTROL	0x0C

/* Entry mode: increment */
#define HD44780_ENTRY_MODE_SET		0x06

/* Clear */
#define HD44780_CLEAR_DISPLAY		0x01

/* Set DDRAM addr (to be ORed with exact address) */
#define HD44780_SET_DDRAM_ADDR		0x80

/* Set CGRAM addr (to be ORed with exact address) */
#define HD44780_SET_CGRAM_ADDR		0x40

/**
 * Default value for contrats
 */

#define CONTRAST_DEFAULT  25

/**
 * Define slave as a module-wide local to save passing it around,
 * plus we will need it after init for the "hd44780" command.
 */

static struct spi_slave *slave;

/*
 * Write a value into a MAX6957AAX register.
 */

static void max6957aax_write(uint8_t reg, uint8_t value)
{
	uint8_t dout[2];

	dout[0] = reg;
	dout[1] = value;
	gpio_set_value(GPO_15, 0);
	/* do SPI read/write (passing din==dout is OK) */
	spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
	gpio_set_value(GPO_15, 1);
}

/*
 * Read a value from a MAX6957AAX register.
 *
 * According to the MAX6957AAX datasheet, we should release the chip
 * select halfway through the read sequence, when the actual register
 * value is read; but the WORK_92105 hardware prevents the MAX6957AAX
 * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
 * so let's release the CS an hold it again while reading the result.
 */

static uint8_t max6957aax_read(uint8_t reg)
{
	uint8_t dout[2], din[2];

	/* send read command */
	dout[0] = reg | 0x80; /* set bit 7 to indicate read */
	dout[1] = 0;
	gpio_set_value(GPO_15, 0);
	/* do SPI read/write (passing din==dout is OK) */
	spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
	/* latch read command */
	gpio_set_value(GPO_15, 1);
	/* read register -- din = noop on xmit, din[1] = reg on recv */
	din[0] = 0;
	din[1] = 0;
	gpio_set_value(GPO_15, 0);
	/* do SPI read/write (passing din==dout is OK) */
	spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
	/* end of read. */
	gpio_set_value(GPO_15, 1);
	return din[1];
}

static void hd44780_instruction(unsigned long instruction)
{
	max6957aax_write(MAX6957AAX_HD44780_RS, 0);
	max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
	max6957aax_write(MAX6957AAX_HD44780_EN, 1);
	max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
	max6957aax_write(MAX6957AAX_HD44780_EN, 0);
	/* HD44780 takes 37 us for most instructions, 1520 for clear */
	if (instruction == HD44780_CLEAR_DISPLAY)
		udelay(2000);
	else
		udelay(100);
}

static void hd44780_write_char(char c)
{
	max6957aax_write(MAX6957AAX_HD44780_RS, 1);
	max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
	max6957aax_write(MAX6957AAX_HD44780_EN, 1);
	max6957aax_write(MAX6957AAX_HD44780_DATA, c);
	max6957aax_write(MAX6957AAX_HD44780_EN, 0);
	/* HD44780 takes 37 us to write to DDRAM or CGRAM */
	udelay(100);
}

static void hd44780_write_str(char *s)
{
	max6957aax_write(MAX6957AAX_HD44780_RS, 1);
	max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
	while (*s) {
		max6957aax_write(MAX6957AAX_HD44780_EN, 1);
		max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
		max6957aax_write(MAX6957AAX_HD44780_EN, 0);
		s++;
		/* HD44780 takes 37 us to write to DDRAM or CGRAM */
		udelay(100);
	}
}

/*
 * Existing user code might expect these custom characters to be
 * recognized and displayed on the LCD
 */

static u8 char_gen_chars[] = {
	/* #8, empty rectangle */
	0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
	/* #9, filled right arrow */
	0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
	/* #10, filled left arrow */
	0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
	/* #11, up and down arrow */
	0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
	/* #12, plus/minus */
	0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
	/* #13, fat exclamation mark */
	0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
	/* #14, empty square */
	0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
	/* #15, struck out square */
	0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
};

static void hd44780_init_char_gen(void)
{
	int i;

	hd44780_instruction(HD44780_SET_CGRAM_ADDR);

	for (i = 0; i < sizeof(char_gen_chars); i++)
		hd44780_write_char(char_gen_chars[i]);

	hd44780_instruction(HD44780_SET_DDRAM_ADDR);
}

void work_92105_display_init(void)
{
	int claim_err;
	char *display_contrast_str;
	uint8_t display_contrast = CONTRAST_DEFAULT;
	uint8_t enable_backlight = 0x96;

	slave = spi_setup_slave(0, 0, 500000, 0);

	if (!slave) {
		printf("Failed to set up SPI slave\n");
		return;
	}

	claim_err = spi_claim_bus(slave);

	if (claim_err)
		debug("Failed to claim SPI bus: %d\n", claim_err);

	/* enable backlight */
	i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);

	/* set display contrast */
	display_contrast_str = getenv("fwopt_dispcontrast");
	if (display_contrast_str)
		display_contrast = simple_strtoul(display_contrast_str,
			NULL, 10);
	i2c_write(0x2c, 0x00, 1, &display_contrast, 1);

	/* request GPO_15 as an output initially set to 1 */
	gpio_request(GPO_15, "MAX6957_nCS");
	gpio_direction_output(GPO_15, 1);

	/* enable MAX6957 portexpander */
	max6957aax_write(MAX6957_CONF, 0x01);
	/* configure pin 8 as input, pins 9..19 as outputs */
	max6957aax_write(MAX6957_CONF_08_11, 0x56);
	max6957aax_write(MAX6957_CONF_12_15, 0x55);
	max6957aax_write(MAX6957_CONF_16_19, 0x55);

	/* initialize HD44780 */
	max6957aax_write(MAX6957AAX_HD44780_EN, 0);
	hd44780_instruction(HD44780_FUNCTION_SET);
	hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL);
	hd44780_instruction(HD44780_ENTRY_MODE_SET);

	/* write custom character glyphs */
	hd44780_init_char_gen();

	/* Show U-Boot version, date and time as a sign-of-life */
	hd44780_instruction(HD44780_CLEAR_DISPLAY);
	hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0);
	hd44780_write_str(U_BOOT_VERSION);
	hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64);
	hd44780_write_str(U_BOOT_DATE);
	hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20);
	hd44780_write_str(U_BOOT_TIME);
}

#ifdef CONFIG_CMD_MAX6957

static int do_max6957aax(cmd_tbl_t *cmdtp, int flag, int argc,
			 char *const argv[])
{
	int reg, val;

	if (argc != 3)
		return CMD_RET_USAGE;
	switch (argv[1][0]) {
	case 'r':
	case 'R':
		reg = simple_strtoul(argv[2], NULL, 0);
		val = max6957aax_read(reg);
		printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val);
		return 0;
	default:
		reg = simple_strtoul(argv[1], NULL, 0);
		val = simple_strtoul(argv[2], NULL, 0);
		max6957aax_write(reg, val);
		printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val);
		return 0;
	}
	return 1;
}

#ifdef CONFIG_SYS_LONGHELP
static char max6957aax_help_text[] =
	"max6957aax - write or read display register:\n"
		"\tmax6957aax R|r reg - read display register;\n"
		"\tmax6957aax reg val - write display register.";
#endif

U_BOOT_CMD(
	max6957aax, 6, 1, do_max6957aax,
	"SPI MAX6957 display write/read",
	max6957aax_help_text
);
#endif /* CONFIG_CMD_MAX6957 */

#ifdef CONFIG_CMD_HD44760

/*
 * We need the HUSH parser because we need string arguments, and
 * only HUSH can understand them.
 */

#if !defined(CONFIG_HUSH_PARSER)
#error CONFIG_CMD_HD44760 requires CONFIG_HUSH_PARSER
#endif

static int do_hd44780(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
{
	char *cmd;

	if (argc != 3)
		return CMD_RET_USAGE;

	cmd = argv[1];

	if (strcasecmp(cmd, "cmd") == 0)
		hd44780_instruction(simple_strtol(argv[2], NULL, 0));
	else if (strcasecmp(cmd, "data") == 0)
		hd44780_write_char(simple_strtol(argv[2], NULL, 0));
	else if (strcasecmp(cmd, "str") == 0)
		hd44780_write_str(argv[2]);
	return 0;
}

#ifdef CONFIG_SYS_LONGHELP
static char hd44780_help_text[] =
	"hd44780 - control LCD driver:\n"
		"\thd44780 cmd <val> - send command <val> to driver;\n"
		"\thd44780 data <val> - send data <val> to driver;\n"
		"\thd44780 str \"<text>\" - send \"<text>\" to driver.";
#endif

U_BOOT_CMD(
	hd44780, 6, 1, do_hd44780,
	"HD44780 LCD driver control",
	hd44780_help_text
);
#endif /* CONFIG_CMD_HD44780 */
OpenPOWER on IntegriCloud