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 */ 231 display_contrast_str = getenv("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 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 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_HD44780 */ 350