Fix the size of the property fields.
[qemu/navara.git] / hw / hd44780.c
blob36f7c215352f9418e2a964d28cc691a3f2e3c9fc
1 /*
2 * HD44780 LCD Controller
4 * Copyright (c) 2009 Filip Navara
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 #include "sysbus.h"
26 #include "console.h"
27 #include "pixel_ops.h"
29 /* pin mapping:
30 8x Data
31 Register Select (input only)
32 Read/Write (input only)
33 Enable (input only)
34 Backlight (input only) */
36 #define D(x) x
38 #define PIN_RS 8
39 #define PIN_RW 9
40 #define PIN_E 10
41 #define PIN_BL 11
43 #define LCD_CLR 1 /* DB0: clear display */
44 #define LCD_HOME 2 /* DB1: return to home position */
45 #define LCD_ENTRY_MODE 4 /* DB2: set entry mode */
46 #define LCD_ENTRY_INC 2 /* DB1: increment */
47 #define LCD_ENTRY_SHIFT 1 /* DB2: shift */
48 #define LCD_ON_CTRL 8 /* DB3: turn lcd/cursor on */
49 #define LCD_ON_DISPLAY 4 /* DB2: turn display on */
50 #define LCD_ON_CURSOR 2 /* DB1: turn cursor on */
51 #define LCD_ON_BLINK 1 /* DB0: blinking cursor */
52 #define LCD_MOVE 16 /* DB4: move cursor/display */
53 #define LCD_MOVE_DISP 8 /* DB3: move display (0-> move cursor) */
54 #define LCD_MOVE_RIGHT 4 /* DB2: move right (0-> left) */
55 #define LCD_FUNCTION 32 /* DB5: function set */
56 #define LCD_FUNCTION_8BIT 16 /* DB4: set 8BIT mode (0->4BIT mode) */
57 #define LCD_FUNCTION_2LINES 8 /* DB3: two lines (0->one line) */
58 #define LCD_FUNCTION_10DOTS 4 /* DB2: 5x10 font (0->5x7 font) */
59 #define LCD_CGRAM 64 /* DB6: set CG RAM address */
60 #define LCD_DDRAM 128 /* DB7: set DD RAM address */
61 #define LCD_BUSY 64 /* DB7: LCD is busy */
63 #define WIDTH 20
64 #define HEIGHT 4
65 #define CZOOM 3
67 uint8_t font5x7[][7]={
68 /* */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00},
69 /* ! */ {0x04,0x04,0x04,0x04,0x00,0x00,0x04},
70 /* " */ {0x0A,0x0A,0x0A,0x00,0x00,0x00,0x00},
71 /* # */ {0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A},
72 /* $ */ {0x04,0x0F,0x14,0x0E,0x05,0x1E,0x04},
73 /* % */ {0x18,0x19,0x02,0x04,0x08,0x13,0x03},
74 /* & */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00},
75 /* ' */ {0x0C,0x04,0x08,0x00,0x00,0x00,0x00},
76 /* ( */ {0x02,0x04,0x08,0x08,0x08,0x04,0x02},
77 /* ) */ {0x08,0x04,0x02,0x02,0x02,0x04,0x08},
78 /* * */ {0x00,0x04,0x15,0x0E,0x15,0x04,0x00},
79 /* + */ {0x00,0x04,0x04,0x1F,0x04,0x04,0x00},
80 /* , */ {0x00,0x00,0x00,0x00,0x0C,0x04,0x08},
81 /* - */ {0x00,0x00,0x00,0x1F,0x00,0x00,0x00},
82 /* . */ {0x00,0x00,0x00,0x00,0x00,0x0C,0x0C},
83 /* / */ {0x00,0x01,0x02,0x04,0x08,0x10,0x00},
84 /* 0 */ {0x0E,0x11,0x13,0x15,0x19,0x11,0x0E},
85 /* 1 */ {0x04,0x0C,0x04,0x04,0x04,0x04,0x0E},
86 /* 2 */ {0x0E,0x11,0x01,0x02,0x04,0x08,0x1F},
87 /* 3 */ {0x1F,0x02,0x04,0x02,0x01,0x11,0x0E},
88 /* 4 */ {0x02,0x06,0x0A,0x12,0x1F,0x02,0x02},
89 /* 5 */ {0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E},
90 /* 6 */ {0x06,0x08,0x10,0x1E,0x11,0x11,0x0E},
91 /* 7 */ {0x1F,0x01,0x02,0x04,0x08,0x08,0x08},
92 /* 8 */ {0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E},
93 /* 9 */ {0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C},
94 /* : */ {0x00,0x0C,0x0C,0x00,0x0C,0x0C,0x00},
95 /* ; */ {0x00,0x0C,0x0C,0x00,0x0C,0x04,0x08},
96 /* < */ {0x02,0x04,0x08,0x10,0x08,0x04,0x02},
97 /* = */ {0x00,0x00,0x1F,0x00,0x1F,0x00,0x00},
98 /* > */ {0x08,0x04,0x02,0x01,0x02,0x04,0x08},
99 /* ? */ {0x0E,0x11,0x01,0x02,0x04,0x00,0x04},
100 /* @ */ {0x0E,0x11,0x01,0x0D,0x15,0x15,0x0E},
101 /* A */ {0x0E,0x11,0x11,0x11,0x1F,0x11,0x11},
102 /* B */ {0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E},
103 /* C */ {0x0E,0x11,0x10,0x10,0x10,0x11,0x0E},
104 /* D */ {0x1C,0x12,0x11,0x11,0x11,0x12,0x1C},
105 /* E */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F},
106 /* F */ {0x1F,0x10,0x10,0x1E,0x10,0x10,0x10},
107 /* G */ {0x0E,0x11,0x10,0x17,0x11,0x11,0x0F},
108 /* H */ {0x11,0x11,0x11,0x1F,0x11,0x11,0x11},
109 /* I */ {0x0E,0x04,0x04,0x04,0x04,0x04,0x0E},
110 /* J */ {0x07,0x02,0x02,0x02,0x02,0x12,0x0C},
111 /* K */ {0x11,0x12,0x14,0x18,0x14,0x12,0x11},
112 /* L */ {0x10,0x10,0x10,0x10,0x10,0x10,0x1F},
113 /* M */ {0x11,0x1B,0x15,0x15,0x11,0x11,0x11},
114 /* N */ {0x11,0x11,0x19,0x15,0x13,0x11,0x11},
115 /* O */ {0x0E,0x11,0x11,0x11,0x11,0x11,0x0E},
116 /* P */ {0x1E,0x11,0x11,0x1E,0x10,0x10,0x10},
117 /* Q */ {0x0E,0x11,0x11,0x11,0x15,0x12,0x0D},
118 /* R */ {0x1E,0x11,0x11,0x1E,0x14,0x12,0x11},
119 /* S */ {0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E},
120 /* T */ {0x1F,0x04,0x04,0x04,0x04,0x04,0x04},
121 /* U */ {0x11,0x11,0x11,0x11,0x11,0x11,0x0E},
122 /* V */ {0x11,0x11,0x11,0x11,0x11,0x0A,0x04},
123 /* W */ {0x11,0x11,0x11,0x15,0x15,0x15,0x0A},
124 /* X */ {0x11,0x11,0x0A,0x04,0x0A,0x11,0x11},
125 /* Y */ {0x11,0x11,0x11,0x0A,0x04,0x04,0x04},
126 /* Z */ {0x1F,0x01,0x02,0x04,0x08,0x10,0x1F},
127 /* [ */ {0x0E,0x08,0x08,0x08,0x08,0x08,0x0E},
128 /* \ */ {0x00,0x10,0x08,0x04,0x02,0x01,0x00},
129 /* ] */ {0x0E,0x02,0x02,0x02,0x02,0x02,0x0E},
130 /* ^ */ {0x04,0x0A,0x11,0x00,0x00,0x00,0x00},
131 /* _ */ {0x00,0x00,0x00,0x00,0x00,0x00,0x1F},
132 /* ` */ {0x08,0x04,0x02,0x00,0x00,0x00,0x00},
133 /* a */ {0x00,0x00,0x0E,0x01,0x0F,0x11,0x0F},
134 /* b */ {0x10,0x10,0x10,0x16,0x19,0x11,0x1E},
135 /* c */ {0x00,0x00,0x0E,0x10,0x10,0x11,0x0E},
136 /* d */ {0x01,0x01,0x01,0x0D,0x13,0x11,0x0F},
137 /* e */ {0x00,0x00,0x0E,0x11,0x1F,0x10,0x0E},
138 /* f */ {0x06,0x09,0x08,0x1C,0x08,0x08,0x08},
139 /* g */ {0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E},
140 /* h */ {0x10,0x10,0x16,0x19,0x11,0x11,0x11},
141 /* i */ {0x00,0x04,0x00,0x04,0x04,0x04,0x04},
142 /* j */ {0x02,0x00,0x06,0x02,0x02,0x12,0x0C},
143 /* k */ {0x10,0x10,0x12,0x14,0x18,0x14,0x12},
144 /* l */ {0x04,0x04,0x04,0x04,0x04,0x04,0x0F},
145 /* m */ {0x00,0x00,0x1A,0x15,0x15,0x11,0x11},
146 /* n */ {0x00,0x00,0x16,0x19,0x11,0x11,0x11},
147 /* o */ {0x00,0x00,0x0E,0x11,0x11,0x11,0x0E},
148 /* p */ {0x00,0x00,0x1E,0x11,0x1E,0x10,0x10},
149 /* q */ {0x00,0x00,0x0D,0x13,0x0F,0x01,0x01},
150 /* r */ {0x00,0x00,0x16,0x19,0x10,0x10,0x10},
151 /* s */ {0x00,0x00,0x0E,0x10,0x0E,0x01,0x1E},
152 /* t */ {0x08,0x08,0x1C,0x08,0x08,0x09,0x06},
153 /* u */ {0x00,0x00,0x11,0x11,0x11,0x13,0x0D},
154 /* v */ {0x00,0x00,0x11,0x11,0x11,0x0A,0x04},
155 /* w */ {0x00,0x00,0x11,0x11,0x15,0x15,0x0A},
156 /* x */ {0x00,0x00,0x11,0x0A,0x04,0x0A,0x11},
157 /* y */ {0x00,0x00,0x11,0x11,0x0F,0x01,0x0E},
158 /* z */ {0x00,0x00,0x1F,0x02,0x04,0x08,0x1F},
159 /* { */ {0x02,0x04,0x04,0x08,0x04,0x04,0x02},
160 /* | */ {0x04,0x04,0x04,0x04,0x04,0x04,0x04},
161 /* } */ {0x08,0x04,0x04,0x02,0x04,0x04,0x08},
164 typedef struct LCDState {
165 SysBusDevice busdev;
166 qemu_irq out[8];
167 DisplayState *ds;
169 uint8_t input;
170 uint32_t control : 1;
171 uint32_t rw : 1;
172 uint32_t backlight : 1;
174 uint8_t mode8bit : 1;
175 uint8_t write_low : 1;
177 uint8_t ac; /* address counter */
178 uint8_t dispcol; /* first visible column (display shift!) */
179 uint8_t id : 1; /* cursor move increase(1)/decrease(0) */
180 uint8_t sh : 1; /* shift display(1) */
181 uint8_t ddram : 1; /* access ddram(1)/cgram(0) */
183 uint8_t display : 1;
184 uint8_t cursor : 1;
185 uint8_t blink : 1;
187 uint8_t two_lines : 1;
188 uint8_t font5x10 : 1;
190 uint8_t need_update : 1;
191 char data[20 * 4];
192 } LCDState;
194 static void draw_char(DisplayState *ds, int x, int y, char ch, uint32_t color, uint32_t backcolor)
196 uint8_t *d;
197 uint8_t cdata;
198 int i, bpp, line;
200 bpp = (ds_get_bits_per_pixel(ds) + 7) >> 3;
201 for (line = 0; line < 7 * CZOOM; line++) {
202 d = ds_get_data(ds) + ds_get_linesize(ds) * y + bpp * x;
203 if (ch >= ' ' && (ch - ' ') < sizeof(font5x7)/sizeof(font5x7[0]))
204 cdata = font5x7[(int)(ch - ' ')][line / CZOOM];
205 else
206 cdata = font5x7[0][line / CZOOM];
207 switch(bpp) {
208 case 1:
209 d += 5 * CZOOM;
210 for (i = 0; i < 5 * CZOOM; i++) {
211 *((uint8_t *)d) = (cdata & (1 << (i / CZOOM))) ? color : backcolor;
212 d--;
214 break;
215 case 2:
216 d += 10 * CZOOM;
217 for (i = 0; i < 5 * CZOOM; i++) {
218 *((uint16_t *)d) = (cdata & (1 << (i / CZOOM))) ? color : backcolor;
219 d -= 2;
221 break;
222 case 4:
223 d += 20 * CZOOM;
224 for (i = 0; i < 5 * CZOOM; i++) {
225 *((uint32_t *)d) = (cdata & (1 << (i / CZOOM))) ? color : backcolor;
226 d -= 4;
228 break;
230 y++;
234 static void hd44780_enable(LCDState *s)
236 if (s->control) {
237 //D(printf("CONTROL: %x\n", s->input));
238 if (s->input & LCD_DDRAM) {
239 int ddram_addr = s->input & ~LCD_DDRAM;
240 if (ddram_addr >= 0 && ddram_addr < WIDTH)
241 s->ac = ddram_addr;
242 else if (ddram_addr >= 64 && ddram_addr < 64 + WIDTH)
243 s->ac = (ddram_addr - 64) + WIDTH;
244 else if (ddram_addr >= 20 && ddram_addr < 20 + WIDTH)
245 s->ac = (ddram_addr - 20) + WIDTH * 2;
246 else if (ddram_addr >= 84 && ddram_addr < 84 + WIDTH)
247 s->ac = (ddram_addr - 84) + WIDTH * 3;
248 s->ddram = 1;
249 } else if (s->input & LCD_CGRAM) {
250 s->ac = s->input & ~LCD_CGRAM;
251 s->ddram = 0;
252 } else if (s->input & LCD_FUNCTION) {
253 s->mode8bit = !!(s->input & LCD_FUNCTION_8BIT);
254 s->two_lines = !!(s->input & LCD_FUNCTION_2LINES);
255 s->font5x10 = !!(s->input & LCD_FUNCTION_10DOTS);
256 } else if (s->input & LCD_MOVE) {
257 if (s->input & LCD_MOVE_DISP) {
258 if (s->input & LCD_MOVE_RIGHT) {
259 s->dispcol--;
260 } else {
261 s->dispcol++;
263 s->dispcol %= sizeof(s->data);
264 } else {
265 // ...
267 } else if (s->input & LCD_ON_CTRL) {
268 s->display = !!(s->input & LCD_ON_DISPLAY);
269 s->cursor = !!(s->input & LCD_ON_CURSOR);
270 s->blink = !!(s->input & LCD_ON_BLINK);
271 } else if (s->input & LCD_ENTRY_MODE) {
272 s->id = !!(s->input & LCD_ENTRY_INC);
273 s->sh = !!(s->input & LCD_ENTRY_SHIFT);
274 } else if (s->input & LCD_HOME) {
275 s->ac = 0;
276 s->dispcol = 0;
277 s->ddram = 1;
278 } else if (s->input & LCD_CLR) {
279 memset(s->data, 32, sizeof(s->data));
280 s->ac = 0;
281 s->dispcol = 0;
282 s->id = 1;
283 s->ddram = 1;
285 } else {
286 if (s->ddram) {
287 s->data[s->ac] = s->input;
288 s->ac++;
289 s->ac %= sizeof(s->data);
290 if (s->sh) {
291 if (s->id) {
292 s->dispcol++;
293 } else {
294 s->dispcol--;
296 s->dispcol %= sizeof(s->data);
298 s->need_update = 1;
299 } else {
300 // ...
305 static void hd44780_set_pin(void *opaque, int pin, int level)
307 LCDState *s = opaque;
309 if (pin >= 0 && pin <= 7) {
310 if (!s->rw) {
311 if (!s->mode8bit && s->write_low)
312 pin -= 4;
313 if (level)
314 s->input |= 1 << pin;
315 else
316 s->input &= ~(1 << pin);
318 } else if (pin == PIN_RS) {
319 s->control = !level;
320 } else if (pin == PIN_RW) {
321 s->rw = level;
322 } else if (pin == PIN_E) {
323 if (!level && !s->rw) {
324 if (!s->mode8bit) {
325 s->write_low = !s->write_low;
326 if (!s->write_low)
327 hd44780_enable(s);
328 } else {
329 hd44780_enable(s);
332 } else if (pin == PIN_BL) {
333 s->backlight = level;
334 s->need_update = 1;
338 static void hd44780_update_display(void *opaque)
340 LCDState *s = opaque;
341 uint32_t color_segment, color_led;
342 int y, x, r, g, b;
344 if (s->need_update) {
345 if (s->backlight) {
346 r = 0;
347 g = 0xff;
348 b = 0x80;
349 } else {
350 r = 0xf0;
351 g = 0xe0;
352 b = 0xb0;
355 switch (ds_get_bits_per_pixel(s->ds)) {
356 case 8:
357 color_segment = rgb_to_pixel8(0, 0, 0);
358 color_led = rgb_to_pixel8(r, g, b);
359 break;
360 case 15:
361 color_segment = rgb_to_pixel15(0, 0, 0);
362 color_led = rgb_to_pixel15(r, g, b);
363 break;
364 case 16:
365 color_segment = rgb_to_pixel16(0, 0, 0);
366 color_led = rgb_to_pixel16(r, g, b);
367 break;
368 case 24:
369 color_segment = rgb_to_pixel24(0, 0, 0);
370 color_led = rgb_to_pixel24(r, g, b);
371 break;
372 case 32:
373 color_segment = rgb_to_pixel32(0, 0, 0);
374 color_led = rgb_to_pixel32(r, g, b);
375 break;
376 default:
377 return;
380 if (s->display) {
381 for (y = 0; y < HEIGHT; y++) {
382 for (x = 0; x < WIDTH; x++) {
383 draw_char(s->ds, x * 5 * CZOOM, y * 7 * CZOOM, s->data[y * WIDTH + x], color_segment, color_led);
388 dpy_update(s->ds, 0, 0, WIDTH * CZOOM * 5, HEIGHT * CZOOM * 7);
392 static void hd44780_invalidate_display(void * opaque)
394 LCDState *s = opaque;
395 s->need_update = 1;
398 static void hd44780_init(SysBusDevice *dev)
400 LCDState *s = FROM_SYSBUS(LCDState, dev);
402 s->need_update = 1;
404 qdev_init_gpio_in(&dev->qdev, hd44780_set_pin, 12);
405 qdev_init_gpio_out(&dev->qdev, s->out, 8);
407 s->ds = graphic_console_init(hd44780_update_display,
408 hd44780_invalidate_display,
409 NULL, NULL, s);
410 qemu_console_resize(s->ds, WIDTH * CZOOM * 5, HEIGHT * CZOOM * 7);
413 static void hd44780_register(void)
415 sysbus_register_dev("gpio,hd44780", sizeof(LCDState),
416 hd44780_init);
419 device_init(hd44780_register)