uboot/board/work-microwave/work_92105/work_92105_display.c
<<
>>
Prefs
   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 <env.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
  82static struct spi_slave *slave;
  83
  84/*
  85 * Write a value into a MAX6957AAX register.
  86 */
  87
  88static 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
 110static 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
 133static 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
 147static 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
 158static 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
 177static 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
 196static 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
 208void 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
 269static 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
 294static 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
 300U_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
 318static 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
 337static 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
 344U_BOOT_CMD(
 345        hd44780, 6, 1, do_hd44780,
 346        "HD44780 LCD driver control",
 347        hd44780_help_text
 348);
 349#endif /* CONFIG_CMD_HD44760 */
 350