From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1MR5qf-0006x4-K8 for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:45 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1MR5qa-0006te-9N for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:45 -0400 Received: from [199.232.76.173] (port=53486 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1MR5qa-0006tK-0P for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:40 -0400 Received: from [82.113.48.144] (port=4824 helo=FilipNavara-PC) by monty-python.gnu.org with smtp (Exim 4.60) (envelope-from ) id 1MR5qY-0000e2-Tf for qemu-devel@nongnu.org; Wed, 15 Jul 2009 10:52:39 -0400 Date: Wed, 15 Jul 09 16:52:36 Central Europe Standard Time From: Filip Navara Sender: Filip Navara MIME-Version: 1.0 Content-Type: text/plain; Message-Id: Subject: [Qemu-devel] [PATCH 14/14] HD44780 LCD Controller List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Only very simple emulation of the LCD controller is included. The controller features the standard HD44780 interface and supports character output on four lines using a static 5x7 font and optional backlight controlled by signal on a separate pin. Capabilities such as custom characters, cursor and 5x10 fonts are not implemented. Saving/loading of device state is not implemented either. Signed-off-by: Filip Navara --- Makefile.target | 2 +- hw/at91pes.c | 10 ++ hw/hd44780.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 429 insertions(+), 1 deletions(-) create mode 100644 hw/hd44780.c diff --git a/Makefile.target b/Makefile.target index dca5340..74d7d5e 100644 --- a/Makefile.target +++ b/Makefile.target @@ -668,7 +668,7 @@ obj-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o obj-y += syborg_virtio.o obj-y += at91_aic.o at91_dbgu.o at91_pio.o at91_pit.o at91_pmc.o at91_rtt.o obj-y += at91_rstc.o at91_intor.o at91_tc.o at91_emac.o at91pes.o -obj-y += gpio_rotary.o gpio_keypad.o +obj-y += gpio_rotary.o gpio_keypad.o hd44780.o CPPFLAGS += -DHAS_AUDIO endif ifeq ($(TARGET_BASE_ARCH), sh4) diff --git a/hw/at91pes.c b/hw/at91pes.c index 544eaaf..b58cc25 100644 --- a/hw/at91pes.c +++ b/hw/at91pes.c @@ -95,6 +95,16 @@ static void at91pes_init(ram_addr_t ram_size, sysbus_mmio_map(sysbus_from_qdev(dev), 0, 0xFFFDC000); sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[16]); + dev = qdev_create(NULL, "gpio,hd44780"); + qdev_init(dev); + for (i = 0; i < 3; i++) + qdev_connect_gpio_out(pioa, 15 - i, qdev_get_gpio_in(dev, 8 + i)); + qdev_connect_gpio_out(piob, 28, qdev_get_gpio_in(dev, 11)); + for (i = 0; i < 4; i++) { + qdev_connect_gpio_out(pioa, 5 + i, qdev_get_gpio_in(dev, 4 + i)); + qdev_connect_gpio_out(dev, 4 + i, qdev_get_gpio_in(pioa, 5 + i)); + } + dev = qdev_create(NULL, "gpio,keypad"); qdev_set_prop_ptr(dev, "keys", keys); qdev_init(dev); diff --git a/hw/hd44780.c b/hw/hd44780.c new file mode 100644 index 0000000..fc84f9d --- /dev/null +++ b/hw/hd44780.c @@ -0,0 +1,418 @@ +/* + * HD44780 LCD Controller + * + * Copyright (c) 2009 Filip Navara + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "console.h" +#include "pixel_ops.h" + +/* pin mapping: + 8 Data + Register Select (input only) + Read/Write (input only) + Enable (input only) + Backlight (input only) */ + +#define D(x) x + +#define PIN_RS 8 +#define PIN_RW 9 +#define PIN_E 10 +#define PIN_BL 11 + +#define LCD_CLR 1 /* DB0: clear display */ +#define LCD_HOME 2 /* DB1: return to home position */ +#define LCD_ENTRY_MODE 4 /* DB2: set entry mode */ +#define LCD_ENTRY_INC 2 /* DB1: increment */ +#define LCD_ENTRY_SHIFT 1 /* DB2: shift */ +#define LCD_ON_CTRL 8 /* DB3: turn lcd/cursor on */ +#define LCD_ON_DISPLAY 4 /* DB2: turn display on */ +#define LCD_ON_CURSOR 2 /* DB1: turn cursor on */ +#define LCD_ON_BLINK 1 /* DB0: blinking cursor */ +#define LCD_MOVE 16 /* DB4: move cursor/display */ +#define LCD_MOVE_DISP 8 /* DB3: move display (0-> move cursor) */ +#define LCD_MOVE_RIGHT 4 /* DB2: move right (0-> left) */ +#define LCD_FUNCTION 32 /* DB5: function set */ +#define LCD_FUNCTION_8BIT 16 /* DB4: set 8BIT mode (0->4BIT mode) */ +#define LCD_FUNCTION_2LINES 8 /* DB3: two lines (0->one line) */ +#define LCD_FUNCTION_10DOTS 4 /* DB2: 5x10 font (0->5x7 font) */ +#define LCD_CGRAM 64 /* DB6: set CG RAM address */ +#define LCD_DDRAM 128 /* DB7: set DD RAM address */ +#define LCD_BUSY 64 /* DB7: LCD is busy */ + +#define WIDTH 20 +#define HEIGHT 4 +#define CZOOM 3 + +uint8_t font5x7[][7]={ +/* */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, +/* ! */ {0x04,0x04,0x04,0x04,0x00,0x00,0x04}, +/* " */ {0x0A,0x0A,0x0A,0x00,0x00,0x00,0x00}, +/* # */ {0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A}, +/* $ */ {0x04,0x0F,0x14,0x0E,0x05,0x1E,0x04}, +/* % */ {0x18,0x19,0x02,0x04,0x08,0x13,0x03}, +/* & */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00}, +/* ' */ {0x0C,0x04,0x08,0x00,0x00,0x00,0x00}, +/* ( */ {0x02,0x04,0x08,0x08,0x08,0x04,0x02}, +/* ) */ {0x08,0x04,0x02,0x02,0x02,0x04,0x08}, +/* * */ {0x00,0x04,0x15,0x0E,0x15,0x04,0x00}, +/* + */ {0x00,0x04,0x04,0x1F,0x04,0x04,0x00}, +/* , */ {0x00,0x00,0x00,0x00,0x0C,0x04,0x08}, +/* - */ {0x00,0x00,0x00,0x1F,0x00,0x00,0x00}, +/* . */ {0x00,0x00,0x00,0x00,0x00,0x0C,0x0C}, +/* / */ {0x00,0x01,0x02,0x04,0x08,0x10,0x00}, +/* 0 */ {0x0E,0x11,0x13,0x15,0x19,0x11,0x0E}, +/* 1 */ {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E}, +/* 2 */ {0x0E,0x11,0x01,0x02,0x04,0x08,0x1F}, +/* 3 */ {0x1F,0x02,0x04,0x02,0x01,0x11,0x0E}, +/* 4 */ {0x02,0x06,0x0A,0x12,0x1F,0x02,0x02}, +/* 5 */ {0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E}, +/* 6 */ {0x06,0x08,0x10,0x1E,0x11,0x11,0x0E}, +/* 7 */ {0x1F,0x01,0x02,0x04,0x08,0x08,0x08}, +/* 8 */ {0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E}, +/* 9 */ {0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C}, +/* : */ {0x00,0x0C,0x0C,0x00,0x0C,0x0C,0x00}, +/* ; */ {0x00,0x0C,0x0C,0x00,0x0C,0x04,0x08}, +/* < */ {0x02,0x04,0x08,0x10,0x08,0x04,0x02}, +/* = */ {0x00,0x00,0x1F,0x00,0x1F,0x00,0x00}, +/* > */ {0x08,0x04,0x02,0x01,0x02,0x04,0x08}, +/* ? */ {0x0E,0x11,0x01,0x02,0x04,0x00,0x04}, +/* @ */ {0x0E,0x11,0x01,0x0D,0x15,0x15,0x0E}, +/* A */ {0x0E,0x11,0x11,0x11,0x1F,0x11,0x11}, +/* B */ {0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E}, +/* C */ {0x0E,0x11,0x10,0x10,0x10,0x11,0x0E}, +/* D */ {0x1C,0x12,0x11,0x11,0x11,0x12,0x1C}, +/* E */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F}, +/* F */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x10}, +/* G */ {0x0E,0x11,0x10,0x17,0x11,0x11,0x0F}, +/* H */ {0x11,0x11,0x11,0x1F,0x11,0x11,0x11}, +/* I */ {0x0E,0x04,0x04,0x04,0x04,0x04,0x0E}, +/* J */ {0x07,0x02,0x02,0x02,0x02,0x12,0x0C}, +/* K */ {0x11,0x12,0x14,0x18,0x14,0x12,0x11}, +/* L */ {0x10,0x10,0x10,0x10,0x10,0x10,0x1F}, +/* M */ {0x11,0x1B,0x15,0x15,0x11,0x11,0x11}, +/* N */ {0x11,0x11,0x19,0x15,0x13,0x11,0x11}, +/* O */ {0x0E,0x11,0x11,0x11,0x11,0x11,0x0E}, +/* P */ {0x1E,0x11,0x11,0x1E,0x10,0x10,0x10}, +/* Q */ {0x0E,0x11,0x11,0x11,0x15,0x12,0x0D}, +/* R */ {0x1E,0x11,0x11,0x1E,0x14,0x12,0x11}, +/* S */ {0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E}, +/* T */ {0x1F,0x04,0x04,0x04,0x04,0x04,0x04}, +/* U */ {0x11,0x11,0x11,0x11,0x11,0x11,0x0E}, +/* V */ {0x11,0x11,0x11,0x11,0x11,0x0A,0x04}, +/* W */ {0x11,0x11,0x11,0x15,0x15,0x15,0x0A}, +/* X */ {0x11,0x11,0x0A,0x04,0x0A,0x11,0x11}, +/* Y */ {0x11,0x11,0x11,0x0A,0x04,0x04,0x04}, +/* Z */ {0x1F,0x01,0x02,0x04,0x08,0x10,0x1F}, +/* [ */ {0x0E,0x08,0x08,0x08,0x08,0x08,0x0E}, +/* \ */ {0x00,0x10,0x08,0x04,0x02,0x01,0x00}, +/* ] */ {0x0E,0x02,0x02,0x02,0x02,0x02,0x0E}, +/* ^ */ {0x04,0x0A,0x11,0x00,0x00,0x00,0x00}, +/* _ */ {0x00,0x00,0x00,0x00,0x00,0x00,0x1F}, +/* ` */ {0x08,0x04,0x02,0x00,0x00,0x00,0x00}, +/* a */ {0x00,0x00,0x0E,0x01,0x0F,0x11,0x0F}, +/* b */ {0x10,0x10,0x10,0x16,0x19,0x11,0x1E}, +/* c */ {0x00,0x00,0x0E,0x10,0x10,0x11,0x0E}, +/* d */ {0x01,0x01,0x01,0x0D,0x13,0x11,0x0F}, +/* e */ {0x00,0x00,0x0E,0x11,0x1F,0x10,0x0E}, +/* f */ {0x06,0x09,0x08,0x1C,0x08,0x08,0x08}, +/* g */ {0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E}, +/* h */ {0x10,0x10,0x16,0x19,0x11,0x11,0x11}, +/* i */ {0x00,0x04,0x00,0x04,0x04,0x04,0x04}, +/* j */ {0x02,0x00,0x06,0x02,0x02,0x12,0x0C}, +/* k */ {0x10,0x10,0x12,0x14,0x18,0x14,0x12}, +/* l */ {0x04,0x04,0x04,0x04,0x04,0x04,0x0F}, +/* m */ {0x00,0x00,0x1A,0x15,0x15,0x11,0x11}, +/* n */ {0x00,0x00,0x16,0x19,0x11,0x11,0x11}, +/* o */ {0x00,0x00,0x0E,0x11,0x11,0x11,0x0E}, +/* p */ {0x00,0x00,0x1E,0x11,0x1E,0x10,0x10}, +/* q */ {0x00,0x00,0x0D,0x13,0x0F,0x01,0x01}, +/* r */ {0x00,0x00,0x16,0x19,0x10,0x10,0x10}, +/* s */ {0x00,0x00,0x0E,0x10,0x0E,0x01,0x1E}, +/* t */ {0x08,0x08,0x1C,0x08,0x08,0x09,0x06}, +/* u */ {0x00,0x00,0x11,0x11,0x11,0x13,0x0D}, +/* v */ {0x00,0x00,0x11,0x11,0x11,0x0A,0x04}, +/* w */ {0x00,0x00,0x11,0x11,0x15,0x15,0x0A}, +/* x */ {0x00,0x00,0x11,0x0A,0x04,0x0A,0x11}, +/* y */ {0x00,0x00,0x11,0x11,0x0F,0x01,0x0E}, +/* z */ {0x00,0x00,0x1F,0x02,0x04,0x08,0x1F}, +/* { */ {0x02,0x04,0x04,0x08,0x04,0x04,0x02}, +/* | */ {0x04,0x04,0x04,0x04,0x04,0x04,0x04}, +/* } */ {0x08,0x04,0x04,0x02,0x04,0x04,0x08}, +}; + +typedef struct LCDState { + SysBusDevice busdev; + qemu_irq out[8]; + DisplayState *ds; + + uint8_t input; + uint32_t control : 1; + uint32_t rw : 1; + uint32_t backlight : 1; + + uint8_t mode8bit : 1; + uint8_t write_low : 1; + + uint8_t ac; /* address counter */ + uint8_t dispcol; /* first visible column (display shift!) */ + uint8_t id : 1; /* cursor move increase(1)/decrease(0) */ + uint8_t sh : 1; /* shift display(1) */ + uint8_t ddram : 1; /* access ddram(1)/cgram(0) */ + + uint8_t display : 1; + uint8_t cursor : 1; + uint8_t blink : 1; + + uint8_t two_lines : 1; + uint8_t font5x10 : 1; + + uint8_t need_update : 1; + char data[20 * 4]; +} LCDState; + +static void draw_char(DisplayState *ds, int x, int y, char ch, uint32_t color, uint32_t backcolor) +{ + uint8_t *d; + uint8_t cdata; + int i, bpp, line; + + bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3; + for (line = 0; line < 7 * CZOOM; line++) { + d = ds_get_data(ds) + ds_get_linesize(ds) * y + bpp * x; + if (ch >= ' ' && (ch - ' ') < sizeof(font5x7)/sizeof(font5x7[0])) + cdata = font5x7[(int)(ch - ' ')][line / CZOOM]; + else + cdata = font5x7[0][line / CZOOM]; + switch(bpp) { + case 1: + d += 5 * CZOOM; + for (i = 0; i < 5 * CZOOM; i++) { + *((uint8_t *)d) = (cdata & (1 << (i / CZOOM))) ? color : backcolor; + d--; + } + break; + case 2: + d += 10 * CZOOM; + for (i = 0; i < 5 * CZOOM; i++) { + *((uint16_t *)d) = (cdata & (1 << (i / CZOOM))) ? color : backcolor; + d -= 2; + } + break; + case 4: + d += 20 * CZOOM; + for (i = 0; i < 5 * CZOOM; i++) { + *((uint32_t *)d) = (cdata & (1 << (i / CZOOM))) ? color : backcolor; + d -= 4; + } + break; + } + y++; + } +} + +static void hd44780_enable(LCDState *s) +{ + if (s->control) { + //D(printf("CONTROL: %x\n", s->input)); + if (s->input & LCD_DDRAM) { + int ddram_addr = s->input & ~LCD_DDRAM; + if (ddram_addr >= 0 && ddram_addr < WIDTH) + s->ac = ddram_addr; + else if (ddram_addr >= 64 && ddram_addr < 64 + WIDTH) + s->ac = (ddram_addr - 64) + WIDTH; + else if (ddram_addr >= 20 && ddram_addr < 20 + WIDTH) + s->ac = (ddram_addr - 20) + WIDTH * 2; + else if (ddram_addr >= 84 && ddram_addr < 84 + WIDTH) + s->ac = (ddram_addr - 84) + WIDTH * 3; + s->ddram = 1; + } else if (s->input & LCD_CGRAM) { + s->ac = s->input & ~LCD_CGRAM; + s->ddram = 0; + } else if (s->input & LCD_FUNCTION) { + s->mode8bit = !!(s->input & LCD_FUNCTION_8BIT); + s->two_lines = !!(s->input & LCD_FUNCTION_2LINES); + s->font5x10 = !!(s->input & LCD_FUNCTION_10DOTS); + } else if (s->input & LCD_MOVE) { + if (s->input & LCD_MOVE_DISP) { + if (s->input & LCD_MOVE_RIGHT) { + s->dispcol--; + } else { + s->dispcol++; + } + s->dispcol %= sizeof(s->data); + } else { + // ... + } + } else if (s->input & LCD_ON_CTRL) { + s->display = !!(s->input & LCD_ON_DISPLAY); + s->cursor = !!(s->input & LCD_ON_CURSOR); + s->blink = !!(s->input & LCD_ON_BLINK); + } else if (s->input & LCD_ENTRY_MODE) { + s->id = !!(s->input & LCD_ENTRY_INC); + s->sh = !!(s->input & LCD_ENTRY_SHIFT); + } else if (s->input & LCD_HOME) { + s->ac = 0; + s->dispcol = 0; + s->ddram = 1; + } else if (s->input & LCD_CLR) { + memset(s->data, 32, sizeof(s->data)); + s->ac = 0; + s->dispcol = 0; + s->id = 1; + s->ddram = 1; + } + } else { + if (s->ddram) { + s->data[s->ac] = s->input; + s->ac++; + s->ac %= sizeof(s->data); + if (s->sh) { + if (s->id) + s->dispcol++; + else + s->dispcol--; + s->dispcol %= sizeof(s->data); + } + s->need_update = 1; + } else { + // ... + } + } +} + +static void hd44780_set_pin(void *opaque, int pin, int level) +{ + LCDState *s = opaque; + + if (pin >= 0 && pin <= 7) { + if (!s->rw) { + if (!s->mode8bit && s->write_low) + pin -= 4; + if (level) + s->input |= 1 << pin; + else + s->input &= ~(1 << pin); + } + } else if (pin == PIN_RS) { + s->control = !level; + } else if (pin == PIN_RW) { + s->rw = level; + } else if (pin == PIN_E) { + if (!level && !s->rw) { + if (!s->mode8bit) { + s->write_low = !s->write_low; + if (!s->write_low) + hd44780_enable(s); + } else { + hd44780_enable(s); + } + } + } else if (pin == PIN_BL) { + s->backlight = level; + s->need_update = 1; + } +} + +static void hd44780_update_display(void *opaque) +{ + LCDState *s = opaque; + uint32_t color_segment, color_led; + int y, x, r, g, b; + + if (s->need_update) { + if (s->backlight) { + r = 0; + g = 0xff; + b = 0x80; + } else { + r = 0xf0; + g = 0xe0; + b = 0xb0; + } + + switch (ds_get_bits_per_pixel(s->ds)) { + case 8: + color_segment = rgb_to_pixel8(0, 0, 0); + color_led = rgb_to_pixel8(r, g, b); + break; + case 15: + color_segment = rgb_to_pixel15(0, 0, 0); + color_led = rgb_to_pixel15(r, g, b); + break; + case 16: + color_segment = rgb_to_pixel16(0, 0, 0); + color_led = rgb_to_pixel16(r, g, b); + break; + case 24: + color_segment = rgb_to_pixel24(0, 0, 0); + color_led = rgb_to_pixel24(r, g, b); + break; + case 32: + color_segment = rgb_to_pixel32(0, 0, 0); + color_led = rgb_to_pixel32(r, g, b); + break; + default: + return; + } + + if (s->display) { + for (y = 0; y < HEIGHT; y++) { + for (x = 0; x < WIDTH; x++) { + draw_char(s->ds, x * 5 * CZOOM, y * 7 * CZOOM, s->data[y * WIDTH + x], color_segment, color_led); + } + } + } + + dpy_update(s->ds, 0, 0, WIDTH * CZOOM * 5, HEIGHT * CZOOM * 7); + } +} + +static void hd44780_invalidate_display(void * opaque) +{ + LCDState *s = opaque; + s->need_update = 1; +} + +static void hd44780_init(SysBusDevice *dev) +{ + LCDState *s = FROM_SYSBUS(LCDState, dev); + + s->need_update = 1; + + qdev_init_gpio_in(&dev->qdev, hd44780_set_pin, 12); + qdev_init_gpio_out(&dev->qdev, s->out, 8); + + s->ds = graphic_console_init(hd44780_update_display, + hd44780_invalidate_display, + NULL, NULL, s); + qemu_console_resize(s->ds, WIDTH * CZOOM * 5, HEIGHT * CZOOM * 7); +} + +static void hd44780_register(void) +{ + sysbus_register_dev("gpio,hd44780", sizeof(LCDState), + hd44780_init); +} + +device_init(hd44780_register) -- 1.6.3.msysgit.0