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