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 <version.h> 21 #include <vsprintf.h> 22 23 /* 24 * GPO 15 in port 3 is gpio 3*32+15 = 111 25 */ 26 27 #define GPO_15 111 28 29 /** 30 * MAX6957AAX registers that we will be using 31 */ 32 33 #define MAX6957_CONF 0x04 34 35 #define MAX6957_CONF_08_11 0x0A 36 #define MAX6957_CONF_12_15 0x0B 37 #define MAX6957_CONF_16_19 0x0C 38 39 /** 40 * Individual gpio ports (one per gpio) to HD44780 41 */ 42 43 #define MAX6957AAX_HD44780_RS 0x29 44 #define MAX6957AAX_HD44780_R_W 0x2A 45 #define MAX6957AAX_HD44780_EN 0x2B 46 #define MAX6957AAX_HD44780_DATA 0x4C 47 48 /** 49 * Display controller instructions 50 */ 51 52 /* Function set: eight bits, two lines, 8-dot font */ 53 #define HD44780_FUNCTION_SET 0x38 54 55 /* Display ON / OFF: turn display on */ 56 #define HD44780_DISPLAY_ON_OFF_CONTROL 0x0C 57 58 /* Entry mode: increment */ 59 #define HD44780_ENTRY_MODE_SET 0x06 60 61 /* Clear */ 62 #define HD44780_CLEAR_DISPLAY 0x01 63 64 /* Set DDRAM addr (to be ORed with exact address) */ 65 #define HD44780_SET_DDRAM_ADDR 0x80 66 67 /* Set CGRAM addr (to be ORed with exact address) */ 68 #define HD44780_SET_CGRAM_ADDR 0x40 69 70 /** 71 * Default value for contrats 72 */ 73 74 #define CONTRAST_DEFAULT 25 75 76 /** 77 * Define slave as a module-wide local to save passing it around, 78 * plus we will need it after init for the "hd44780" command. 79 */ 80 81 static struct spi_slave *slave; 82 83 /* 84 * Write a value into a MAX6957AAX register. 85 */ 86 87 static void max6957aax_write(uint8_t reg, uint8_t value) 88 { 89 uint8_t dout[2]; 90 91 dout[0] = reg; 92 dout[1] = value; 93 gpio_set_value(GPO_15, 0); 94 /* do SPI read/write (passing din==dout is OK) */ 95 spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END); 96 gpio_set_value(GPO_15, 1); 97 } 98 99 /* 100 * Read a value from a MAX6957AAX register. 101 * 102 * According to the MAX6957AAX datasheet, we should release the chip 103 * select halfway through the read sequence, when the actual register 104 * value is read; but the WORK_92105 hardware prevents the MAX6957AAX 105 * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected. 106 * so let's release the CS an hold it again while reading the result. 107 */ 108 109 static uint8_t max6957aax_read(uint8_t reg) 110 { 111 uint8_t dout[2], din[2]; 112 113 /* send read command */ 114 dout[0] = reg | 0x80; /* set bit 7 to indicate read */ 115 dout[1] = 0; 116 gpio_set_value(GPO_15, 0); 117 /* do SPI read/write (passing din==dout is OK) */ 118 spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END); 119 /* latch read command */ 120 gpio_set_value(GPO_15, 1); 121 /* read register -- din = noop on xmit, din[1] = reg on recv */ 122 din[0] = 0; 123 din[1] = 0; 124 gpio_set_value(GPO_15, 0); 125 /* do SPI read/write (passing din==dout is OK) */ 126 spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END); 127 /* end of read. */ 128 gpio_set_value(GPO_15, 1); 129 return din[1]; 130 } 131 132 static void hd44780_instruction(unsigned long instruction) 133 { 134 max6957aax_write(MAX6957AAX_HD44780_RS, 0); 135 max6957aax_write(MAX6957AAX_HD44780_R_W, 0); 136 max6957aax_write(MAX6957AAX_HD44780_EN, 1); 137 max6957aax_write(MAX6957AAX_HD44780_DATA, instruction); 138 max6957aax_write(MAX6957AAX_HD44780_EN, 0); 139 /* HD44780 takes 37 us for most instructions, 1520 for clear */ 140 if (instruction == HD44780_CLEAR_DISPLAY) 141 udelay(2000); 142 else 143 udelay(100); 144 } 145 146 static void hd44780_write_char(char c) 147 { 148 max6957aax_write(MAX6957AAX_HD44780_RS, 1); 149 max6957aax_write(MAX6957AAX_HD44780_R_W, 0); 150 max6957aax_write(MAX6957AAX_HD44780_EN, 1); 151 max6957aax_write(MAX6957AAX_HD44780_DATA, c); 152 max6957aax_write(MAX6957AAX_HD44780_EN, 0); 153 /* HD44780 takes 37 us to write to DDRAM or CGRAM */ 154 udelay(100); 155 } 156 157 static void hd44780_write_str(char *s) 158 { 159 max6957aax_write(MAX6957AAX_HD44780_RS, 1); 160 max6957aax_write(MAX6957AAX_HD44780_R_W, 0); 161 while (*s) { 162 max6957aax_write(MAX6957AAX_HD44780_EN, 1); 163 max6957aax_write(MAX6957AAX_HD44780_DATA, *s); 164 max6957aax_write(MAX6957AAX_HD44780_EN, 0); 165 s++; 166 /* HD44780 takes 37 us to write to DDRAM or CGRAM */ 167 udelay(100); 168 } 169 } 170 171 /* 172 * Existing user code might expect these custom characters to be 173 * recognized and displayed on the LCD 174 */ 175 176 static u8 char_gen_chars[] = { 177 /* #8, empty rectangle */ 178 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F, 179 /* #9, filled right arrow */ 180 0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00, 181 /* #10, filled left arrow */ 182 0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00, 183 /* #11, up and down arrow */ 184 0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04, 185 /* #12, plus/minus */ 186 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00, 187 /* #13, fat exclamation mark */ 188 0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00, 189 /* #14, empty square */ 190 0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00, 191 /* #15, struck out square */ 192 0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00, 193 }; 194 195 static void hd44780_init_char_gen(void) 196 { 197 int i; 198 199 hd44780_instruction(HD44780_SET_CGRAM_ADDR); 200 201 for (i = 0; i < sizeof(char_gen_chars); i++) 202 hd44780_write_char(char_gen_chars[i]); 203 204 hd44780_instruction(HD44780_SET_DDRAM_ADDR); 205 } 206 207 void work_92105_display_init(void) 208 { 209 int claim_err; 210 char *display_contrast_str; 211 uint8_t display_contrast = CONTRAST_DEFAULT; 212 uint8_t enable_backlight = 0x96; 213 214 slave = spi_setup_slave(0, 0, 500000, 0); 215 216 if (!slave) { 217 printf("Failed to set up SPI slave\n"); 218 return; 219 } 220 221 claim_err = spi_claim_bus(slave); 222 223 if (claim_err) 224 debug("Failed to claim SPI bus: %d\n", claim_err); 225 226 /* enable backlight */ 227 i2c_write(0x2c, 0x01, 1, &enable_backlight, 1); 228 229 /* set display contrast */ 230 display_contrast_str = env_get("fwopt_dispcontrast"); 231 if (display_contrast_str) 232 display_contrast = simple_strtoul(display_contrast_str, 233 NULL, 10); 234 i2c_write(0x2c, 0x00, 1, &display_contrast, 1); 235 236 /* request GPO_15 as an output initially set to 1 */ 237 gpio_request(GPO_15, "MAX6957_nCS"); 238 gpio_direction_output(GPO_15, 1); 239 240 /* enable MAX6957 portexpander */ 241 max6957aax_write(MAX6957_CONF, 0x01); 242 /* configure pin 8 as input, pins 9..19 as outputs */ 243 max6957aax_write(MAX6957_CONF_08_11, 0x56); 244 max6957aax_write(MAX6957_CONF_12_15, 0x55); 245 max6957aax_write(MAX6957_CONF_16_19, 0x55); 246 247 /* initialize HD44780 */ 248 max6957aax_write(MAX6957AAX_HD44780_EN, 0); 249 hd44780_instruction(HD44780_FUNCTION_SET); 250 hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL); 251 hd44780_instruction(HD44780_ENTRY_MODE_SET); 252 253 /* write custom character glyphs */ 254 hd44780_init_char_gen(); 255 256 /* Show U-Boot version, date and time as a sign-of-life */ 257 hd44780_instruction(HD44780_CLEAR_DISPLAY); 258 hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0); 259 hd44780_write_str(U_BOOT_VERSION); 260 hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64); 261 hd44780_write_str(U_BOOT_DATE); 262 hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20); 263 hd44780_write_str(U_BOOT_TIME); 264 } 265 266 #ifdef CONFIG_CMD_MAX6957 267 268 static int do_max6957aax(cmd_tbl_t *cmdtp, int flag, int argc, 269 char *const argv[]) 270 { 271 int reg, val; 272 273 if (argc != 3) 274 return CMD_RET_USAGE; 275 switch (argv[1][0]) { 276 case 'r': 277 case 'R': 278 reg = simple_strtoul(argv[2], NULL, 0); 279 val = max6957aax_read(reg); 280 printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val); 281 return 0; 282 default: 283 reg = simple_strtoul(argv[1], NULL, 0); 284 val = simple_strtoul(argv[2], NULL, 0); 285 max6957aax_write(reg, val); 286 printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val); 287 return 0; 288 } 289 return 1; 290 } 291 292 #ifdef CONFIG_SYS_LONGHELP 293 static char max6957aax_help_text[] = 294 "max6957aax - write or read display register:\n" 295 "\tmax6957aax R|r reg - read display register;\n" 296 "\tmax6957aax reg val - write display register."; 297 #endif 298 299 U_BOOT_CMD( 300 max6957aax, 6, 1, do_max6957aax, 301 "SPI MAX6957 display write/read", 302 max6957aax_help_text 303 ); 304 #endif /* CONFIG_CMD_MAX6957 */ 305 306 #ifdef CONFIG_CMD_HD44760 307 308 /* 309 * We need the HUSH parser because we need string arguments, and 310 * only HUSH can understand them. 311 */ 312 313 #if !defined(CONFIG_HUSH_PARSER) 314 #error CONFIG_CMD_HD44760 requires CONFIG_HUSH_PARSER 315 #endif 316 317 static int do_hd44780(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) 318 { 319 char *cmd; 320 321 if (argc != 3) 322 return CMD_RET_USAGE; 323 324 cmd = argv[1]; 325 326 if (strcasecmp(cmd, "cmd") == 0) 327 hd44780_instruction(simple_strtol(argv[2], NULL, 0)); 328 else if (strcasecmp(cmd, "data") == 0) 329 hd44780_write_char(simple_strtol(argv[2], NULL, 0)); 330 else if (strcasecmp(cmd, "str") == 0) 331 hd44780_write_str(argv[2]); 332 return 0; 333 } 334 335 #ifdef CONFIG_SYS_LONGHELP 336 static char hd44780_help_text[] = 337 "hd44780 - control LCD driver:\n" 338 "\thd44780 cmd <val> - send command <val> to driver;\n" 339 "\thd44780 data <val> - send data <val> to driver;\n" 340 "\thd44780 str \"<text>\" - send \"<text>\" to driver."; 341 #endif 342 343 U_BOOT_CMD( 344 hd44780, 6, 1, do_hd44780, 345 "HD44780 LCD driver control", 346 hd44780_help_text 347 ); 348 #endif /* CONFIG_CMD_HD44760 */ 349