|
@@ -0,0 +1,349 @@
|
|
|
|
+/*
|
|
|
|
+ * work_92105 display support
|
|
|
|
+ *
|
|
|
|
+ * (C) Copyright 2014 DENX Software Engineering GmbH
|
|
|
|
+ * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
|
|
|
|
+ *
|
|
|
|
+ * The work_92105 display is a HD44780-compatible module
|
|
|
|
+ * controlled through a MAX6957AAX SPI port expander, two
|
|
|
|
+ * MAX518 I2C DACs and native LPC32xx GPO 15.
|
|
|
|
+ *
|
|
|
|
+ * SPDX-License-Identifier: GPL-2.0+
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <common.h>
|
|
|
|
+#include <asm/arch/sys_proto.h>
|
|
|
|
+#include <asm/arch/cpu.h>
|
|
|
|
+#include <asm/arch/emc.h>
|
|
|
|
+#include <asm/gpio.h>
|
|
|
|
+#include <spi.h>
|
|
|
|
+#include <i2c.h>
|
|
|
|
+#include <version.h>
|
|
|
|
+#include <vsprintf.h>
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * GPO 15 in port 3 is gpio 3*32+15 = 111
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define GPO_15 111
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * MAX6957AAX registers that we will be using
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define MAX6957_CONF 0x04
|
|
|
|
+
|
|
|
|
+#define MAX6957_CONF_08_11 0x0A
|
|
|
|
+#define MAX6957_CONF_12_15 0x0B
|
|
|
|
+#define MAX6957_CONF_16_19 0x0C
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Individual gpio ports (one per gpio) to HD44780
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define MAX6957AAX_HD44780_RS 0x29
|
|
|
|
+#define MAX6957AAX_HD44780_R_W 0x2A
|
|
|
|
+#define MAX6957AAX_HD44780_EN 0x2B
|
|
|
|
+#define MAX6957AAX_HD44780_DATA 0x4C
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Display controller instructions
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+/* Function set: eight bits, two lines, 8-dot font */
|
|
|
|
+#define HD44780_FUNCTION_SET 0x38
|
|
|
|
+
|
|
|
|
+/* Display ON / OFF: turn display on */
|
|
|
|
+#define HD44780_DISPLAY_ON_OFF_CONTROL 0x0C
|
|
|
|
+
|
|
|
|
+/* Entry mode: increment */
|
|
|
|
+#define HD44780_ENTRY_MODE_SET 0x06
|
|
|
|
+
|
|
|
|
+/* Clear */
|
|
|
|
+#define HD44780_CLEAR_DISPLAY 0x01
|
|
|
|
+
|
|
|
|
+/* Set DDRAM addr (to be ORed with exact address) */
|
|
|
|
+#define HD44780_SET_DDRAM_ADDR 0x80
|
|
|
|
+
|
|
|
|
+/* Set CGRAM addr (to be ORed with exact address) */
|
|
|
|
+#define HD44780_SET_CGRAM_ADDR 0x40
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Default value for contrats
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define CONTRAST_DEFAULT 25
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * Define slave as a module-wide local to save passing it around,
|
|
|
|
+ * plus we will need it after init for the "hd44780" command.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static struct spi_slave *slave;
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Write a value into a MAX6957AAX register.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static void max6957aax_write(uint8_t reg, uint8_t value)
|
|
|
|
+{
|
|
|
|
+ uint8_t dout[2];
|
|
|
|
+
|
|
|
|
+ dout[0] = reg;
|
|
|
|
+ dout[1] = value;
|
|
|
|
+ gpio_set_value(GPO_15, 0);
|
|
|
|
+ /* do SPI read/write (passing din==dout is OK) */
|
|
|
|
+ spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
|
|
|
|
+ gpio_set_value(GPO_15, 1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Read a value from a MAX6957AAX register.
|
|
|
|
+ *
|
|
|
|
+ * According to the MAX6957AAX datasheet, we should release the chip
|
|
|
|
+ * select halfway through the read sequence, when the actual register
|
|
|
|
+ * value is read; but the WORK_92105 hardware prevents the MAX6957AAX
|
|
|
|
+ * SPI OUT from reaching the LPC32XX SIP MISO if chip is not selected.
|
|
|
|
+ * so let's release the CS an hold it again while reading the result.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static uint8_t max6957aax_read(uint8_t reg)
|
|
|
|
+{
|
|
|
|
+ uint8_t dout[2], din[2];
|
|
|
|
+
|
|
|
|
+ /* send read command */
|
|
|
|
+ dout[0] = reg | 0x80; /* set bit 7 to indicate read */
|
|
|
|
+ dout[1] = 0;
|
|
|
|
+ gpio_set_value(GPO_15, 0);
|
|
|
|
+ /* do SPI read/write (passing din==dout is OK) */
|
|
|
|
+ spi_xfer(slave, 16, dout, dout, SPI_XFER_BEGIN | SPI_XFER_END);
|
|
|
|
+ /* latch read command */
|
|
|
|
+ gpio_set_value(GPO_15, 1);
|
|
|
|
+ /* read register -- din = noop on xmit, din[1] = reg on recv */
|
|
|
|
+ din[0] = 0;
|
|
|
|
+ din[1] = 0;
|
|
|
|
+ gpio_set_value(GPO_15, 0);
|
|
|
|
+ /* do SPI read/write (passing din==dout is OK) */
|
|
|
|
+ spi_xfer(slave, 16, din, din, SPI_XFER_BEGIN | SPI_XFER_END);
|
|
|
|
+ /* end of read. */
|
|
|
|
+ gpio_set_value(GPO_15, 1);
|
|
|
|
+ return din[1];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hd44780_instruction(unsigned long instruction)
|
|
|
|
+{
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_RS, 0);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 1);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_DATA, instruction);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
|
|
|
+ /* HD44780 takes 37 us for most instructions, 1520 for clear */
|
|
|
|
+ if (instruction == HD44780_CLEAR_DISPLAY)
|
|
|
|
+ udelay(2000);
|
|
|
|
+ else
|
|
|
|
+ udelay(100);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hd44780_write_char(char c)
|
|
|
|
+{
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_RS, 1);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 1);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_DATA, c);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
|
|
|
+ /* HD44780 takes 37 us to write to DDRAM or CGRAM */
|
|
|
|
+ udelay(100);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void hd44780_write_str(char *s)
|
|
|
|
+{
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_RS, 1);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_R_W, 0);
|
|
|
|
+ while (*s) {
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 1);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_DATA, *s);
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
|
|
|
+ s++;
|
|
|
|
+ /* HD44780 takes 37 us to write to DDRAM or CGRAM */
|
|
|
|
+ udelay(100);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Existing user code might expect these custom characters to be
|
|
|
|
+ * recognized and displayed on the LCD
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+static u8 char_gen_chars[] = {
|
|
|
|
+ /* #8, empty rectangle */
|
|
|
|
+ 0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F,
|
|
|
|
+ /* #9, filled right arrow */
|
|
|
|
+ 0x10, 0x18, 0x1C, 0x1E, 0x1C, 0x18, 0x10, 0x00,
|
|
|
|
+ /* #10, filled left arrow */
|
|
|
|
+ 0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00,
|
|
|
|
+ /* #11, up and down arrow */
|
|
|
|
+ 0x04, 0x0E, 0x1F, 0x00, 0x00, 0x1F, 0x0E, 0x04,
|
|
|
|
+ /* #12, plus/minus */
|
|
|
|
+ 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00, 0x1F, 0x00,
|
|
|
|
+ /* #13, fat exclamation mark */
|
|
|
|
+ 0x06, 0x06, 0x06, 0x06, 0x00, 0x06, 0x06, 0x00,
|
|
|
|
+ /* #14, empty square */
|
|
|
|
+ 0x00, 0x1F, 0x11, 0x11, 0x11, 0x1F, 0x00, 0x00,
|
|
|
|
+ /* #15, struck out square */
|
|
|
|
+ 0x00, 0x1F, 0x19, 0x15, 0x13, 0x1F, 0x00, 0x00,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void hd44780_init_char_gen(void)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ hd44780_instruction(HD44780_SET_CGRAM_ADDR);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < sizeof(char_gen_chars); i++)
|
|
|
|
+ hd44780_write_char(char_gen_chars[i]);
|
|
|
|
+
|
|
|
|
+ hd44780_instruction(HD44780_SET_DDRAM_ADDR);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void work_92105_display_init(void)
|
|
|
|
+{
|
|
|
|
+ int claim_err;
|
|
|
|
+ char *display_contrast_str;
|
|
|
|
+ uint8_t display_contrast = CONTRAST_DEFAULT;
|
|
|
|
+ uint8_t enable_backlight = 0x96;
|
|
|
|
+
|
|
|
|
+ slave = spi_setup_slave(0, 0, 500000, 0);
|
|
|
|
+
|
|
|
|
+ if (!slave) {
|
|
|
|
+ printf("Failed to set up SPI slave\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ claim_err = spi_claim_bus(slave);
|
|
|
|
+
|
|
|
|
+ if (claim_err)
|
|
|
|
+ debug("Failed to claim SPI bus: %d\n", claim_err);
|
|
|
|
+
|
|
|
|
+ /* enable backlight */
|
|
|
|
+ i2c_write(0x2c, 0x01, 1, &enable_backlight, 1);
|
|
|
|
+
|
|
|
|
+ /* set display contrast */
|
|
|
|
+ display_contrast_str = getenv("fwopt_dispcontrast");
|
|
|
|
+ if (display_contrast_str)
|
|
|
|
+ display_contrast = simple_strtoul(display_contrast_str,
|
|
|
|
+ NULL, 10);
|
|
|
|
+ i2c_write(0x2c, 0x00, 1, &display_contrast, 1);
|
|
|
|
+
|
|
|
|
+ /* request GPO_15 as an output initially set to 1 */
|
|
|
|
+ gpio_request(GPO_15, "MAX6957_nCS");
|
|
|
|
+ gpio_direction_output(GPO_15, 1);
|
|
|
|
+
|
|
|
|
+ /* enable MAX6957 portexpander */
|
|
|
|
+ max6957aax_write(MAX6957_CONF, 0x01);
|
|
|
|
+ /* configure pin 8 as input, pins 9..19 as outputs */
|
|
|
|
+ max6957aax_write(MAX6957_CONF_08_11, 0x56);
|
|
|
|
+ max6957aax_write(MAX6957_CONF_12_15, 0x55);
|
|
|
|
+ max6957aax_write(MAX6957_CONF_16_19, 0x55);
|
|
|
|
+
|
|
|
|
+ /* initialize HD44780 */
|
|
|
|
+ max6957aax_write(MAX6957AAX_HD44780_EN, 0);
|
|
|
|
+ hd44780_instruction(HD44780_FUNCTION_SET);
|
|
|
|
+ hd44780_instruction(HD44780_DISPLAY_ON_OFF_CONTROL);
|
|
|
|
+ hd44780_instruction(HD44780_ENTRY_MODE_SET);
|
|
|
|
+
|
|
|
|
+ /* write custom character glyphs */
|
|
|
|
+ hd44780_init_char_gen();
|
|
|
|
+
|
|
|
|
+ /* Show U-Boot version, date and time as a sign-of-life */
|
|
|
|
+ hd44780_instruction(HD44780_CLEAR_DISPLAY);
|
|
|
|
+ hd44780_instruction(HD44780_SET_DDRAM_ADDR | 0);
|
|
|
|
+ hd44780_write_str(U_BOOT_VERSION);
|
|
|
|
+ hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64);
|
|
|
|
+ hd44780_write_str(U_BOOT_DATE);
|
|
|
|
+ hd44780_instruction(HD44780_SET_DDRAM_ADDR | 64 | 20);
|
|
|
|
+ hd44780_write_str(U_BOOT_TIME);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_CMD_MAX6957
|
|
|
|
+
|
|
|
|
+static int do_max6957aax(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
|
|
+ char *const argv[])
|
|
|
|
+{
|
|
|
|
+ int reg, val;
|
|
|
|
+
|
|
|
|
+ if (argc != 3)
|
|
|
|
+ return CMD_RET_USAGE;
|
|
|
|
+ switch (argv[1][0]) {
|
|
|
|
+ case 'r':
|
|
|
|
+ case 'R':
|
|
|
|
+ reg = simple_strtoul(argv[2], NULL, 0);
|
|
|
|
+ val = max6957aax_read(reg);
|
|
|
|
+ printf("MAX6957 reg 0x%02x read 0x%02x\n", reg, val);
|
|
|
|
+ return 0;
|
|
|
|
+ default:
|
|
|
|
+ reg = simple_strtoul(argv[1], NULL, 0);
|
|
|
|
+ val = simple_strtoul(argv[2], NULL, 0);
|
|
|
|
+ max6957aax_write(reg, val);
|
|
|
|
+ printf("MAX6957 reg 0x%02x wrote 0x%02x\n", reg, val);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_SYS_LONGHELP
|
|
|
|
+static char max6957aax_help_text[] =
|
|
|
|
+ "max6957aax - write or read display register:\n"
|
|
|
|
+ "\tmax6957aax R|r reg - read display register;\n"
|
|
|
|
+ "\tmax6957aax reg val - write display register.";
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+U_BOOT_CMD(
|
|
|
|
+ max6957aax, 6, 1, do_max6957aax,
|
|
|
|
+ "SPI MAX6957 display write/read",
|
|
|
|
+ max6957aax_help_text
|
|
|
|
+);
|
|
|
|
+#endif /* CONFIG_CMD_MAX6957 */
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_CMD_HD44760
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * We need the HUSH parser because we need string arguments, and
|
|
|
|
+ * only HUSH can understand them.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#if !defined(CONFIG_SYS_HUSH_PARSER)
|
|
|
|
+#error CONFIG_CMD_HD44760 requires CONFIG_SYS_HUSH_PARSER
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+static int do_hd44780(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
|
|
+{
|
|
|
|
+ char *cmd;
|
|
|
|
+
|
|
|
|
+ if (argc != 3)
|
|
|
|
+ return CMD_RET_USAGE;
|
|
|
|
+
|
|
|
|
+ cmd = argv[1];
|
|
|
|
+
|
|
|
|
+ if (strcasecmp(cmd, "cmd") == 0)
|
|
|
|
+ hd44780_instruction(simple_strtol(argv[2], NULL, 0));
|
|
|
|
+ else if (strcasecmp(cmd, "data") == 0)
|
|
|
|
+ hd44780_write_char(simple_strtol(argv[2], NULL, 0));
|
|
|
|
+ else if (strcasecmp(cmd, "str") == 0)
|
|
|
|
+ hd44780_write_str(argv[2]);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_SYS_LONGHELP
|
|
|
|
+static char hd44780_help_text[] =
|
|
|
|
+ "hd44780 - control LCD driver:\n"
|
|
|
|
+ "\thd44780 cmd <val> - send command <val> to driver;\n"
|
|
|
|
+ "\thd44780 data <val> - send data <val> to driver;\n"
|
|
|
|
+ "\thd44780 str \"<text>\" - send \"<text>\" to driver.";
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+U_BOOT_CMD(
|
|
|
|
+ hd44780, 6, 1, do_hd44780,
|
|
|
|
+ "HD44780 LCD driver control",
|
|
|
|
+ hd44780_help_text
|
|
|
|
+);
|
|
|
|
+#endif /* CONFIG_CMD_HD44780 */
|