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 <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
  81static struct spi_slave *slave;
  82
  83/*
  84 * Write a value into a MAX6957AAX register.
  85 */
  86
  87static 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
 109static 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
 132static 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
 146static 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
 157static 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
 176static 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
 195static 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
 207void 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
 268static 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
 293static 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
 299U_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
 317static 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
 336static 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
 343U_BOOT_CMD(
 344        hd44780, 6, 1, do_hd44780,
 345        "HD44780 LCD driver control",
 346        hd44780_help_text
 347);
 348#endif /* CONFIG_CMD_HD44760 */
 349