Rewrite the framebuffer update display function.
[qemu/mini2440.git] / hw / s3c24xx_lcd.c
blob70cee54d07cfcbeeac22dbe05313090e284b62ae
1 /*
2 * Samsung S3C24xx series LCD controller.
4 * Copyright (c) 2007 OpenMoko, Inc.
5 * Author: Andrzej Zaborowski <andrew@openedhand.com>
6 * With: Michel Pollet <buserror@gmail.com>
8 * This code is licenced under the GNU GPL v2.
9 */
11 #include "s3c.h"
12 #include "hw.h"
13 #include "console.h"
14 #include "framebuffer.h"
17 struct s3c_lcd_state_s {
18 target_phys_addr_t base;
19 void *irq;
20 DisplayState *ds;
21 drawfn *line_fn;
23 uint32_t con[5];
24 uint32_t saddr[3];
25 uint32_t r;
26 uint32_t g;
27 uint16_t b;
28 uint32_t dithmode;
29 uint32_t tpal;
30 uint8_t intpnd;
31 uint8_t srcpnd;
32 uint8_t intmsk;
33 uint8_t lpcsel;
35 uint16_t raw_pal[0x100];
37 int width;
38 int height;
39 int bpp;
40 int enable;
41 int msb;
42 int frm565;
43 uint32_t palette[0x100];
44 int invalidate;
45 int invalidatep;
46 int src_width;
47 int dest_width;
48 drawfn fn;
51 static void s3c_lcd_update(struct s3c_lcd_state_s *s)
53 s->intpnd |= s->srcpnd & ~s->intmsk;
54 qemu_set_irq(s->irq, !!s->intpnd);
57 void s3c_lcd_reset(struct s3c_lcd_state_s *s)
59 s->enable = 0;
60 s->invalidate = 1;
61 s->invalidatep = 1;
62 s->width = -1;
63 s->height = -1;
65 s->con[0] = 0x00000000;
66 s->con[1] = 0x00000000;
67 s->con[2] = 0x00000000;
68 s->con[3] = 0x00000000;
69 s->con[4] = 0x00000000;
70 s->saddr[0] = 0x00000000;
71 s->saddr[1] = 0x00000000;
72 s->saddr[2] = 0x00000000;
73 s->r = 0x00000000;
74 s->g = 0x00000000;
75 s->b = 0x0000;
76 s->dithmode = 0x00000;
77 s->tpal = 0x00000000;
78 s->intpnd = 0;
79 s->srcpnd = 0;
80 s->intmsk = 3;
81 s->lpcsel = 4;
82 s3c_lcd_update(s);
85 #define S3C_LCDCON1 0x00 /* LCD Control register 1 */
86 #define S3C_LCDCON2 0x04 /* LCD Control register 2 */
87 #define S3C_LCDCON3 0x08 /* LCD Control register 3 */
88 #define S3C_LCDCON4 0x0c /* LCD Control register 4 */
89 #define S3C_LCDCON5 0x10 /* LCD Control register 5 */
90 #define S3C_LCDSADDR1 0x14 /* Framebuffer Start Address 1 register */
91 #define S3C_LCDSADDR2 0x18 /* Framebuffer Start Address 2 register */
92 #define S3C_LCDSADDR3 0x1c /* Framebuffer Start Address 3 register */
93 #define S3C_REDLUT 0x20 /* Red Lookup Table register */
94 #define S3C_GREENLUT 0x24 /* Green Lookup Table register */
95 #define S3C_BLUELUT 0x28 /* Blue Lookup Table register */
96 #define S3C_DITHMODE 0x4c /* Dithering Mode register */
97 #define S3C_TPAL 0x50 /* Temporary Palette register */
98 #define S3C_LCDINTPND 0x54 /* LCD Interrupt Pending register */
99 #define S3C_LCDSRCPND 0x58 /* LCD Interrupt Source Pending register */
100 #define S3C_LCDINTMSK 0x5c /* LCD Interrupt Mask register */
101 #define S3C_LPCSEL 0x60 /* LPC3600 Control register */
103 #define S3C_PALETTE 0x400 /* Palette IO start offset */
104 #define S3C_PALETTEEND 0x5ff /* Palette IO end offset */
106 static uint32_t s3c_lcd_read(void *opaque, target_phys_addr_t addr)
108 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
110 switch (addr) {
111 case S3C_LCDCON1:
112 return s->con[0]; /* XXX Return random LINECNT? */
113 case S3C_LCDCON2:
114 return s->con[1];
115 case S3C_LCDCON3:
116 return s->con[2];
117 case S3C_LCDCON4:
118 return s->con[3];
119 case S3C_LCDCON5:
120 return s->con[4]; /* XXX Return random STATUS? */
121 case S3C_LCDSADDR1:
122 return s->saddr[0];
123 case S3C_LCDSADDR2:
124 return s->saddr[1];
125 case S3C_LCDSADDR3:
126 return s->saddr[2];
127 case S3C_REDLUT:
128 return s->r;
129 case S3C_GREENLUT:
130 return s->g;
131 case S3C_BLUELUT:
132 return s->b;
133 case S3C_DITHMODE:
134 return s->dithmode;
135 case S3C_TPAL:
136 return s->tpal;
137 case S3C_LCDINTPND:
138 return s->intpnd;
139 case S3C_LCDSRCPND:
140 return s->srcpnd;
141 case S3C_LCDINTMSK:
142 return s->intmsk;
143 case S3C_LPCSEL:
144 return s->lpcsel;
145 case S3C_PALETTE ... S3C_PALETTEEND:
146 /* XXX assuming 16bit access */
147 return s->raw_pal[(addr - S3C_PALETTE) >> 1];
148 default:
149 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
150 break;
152 return 0;
155 static void s3c_lcd_write(void *opaque, target_phys_addr_t addr,
156 uint32_t value)
158 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
160 switch (addr) {
161 case S3C_LCDCON1:
162 s->con[0] = value & 0x0003ffff;
163 s->enable = value & 1;
164 s->bpp = (value >> 1) & 0xf;
165 s->invalidate = 1;
166 s->invalidatep = 1;
167 break;
168 case S3C_LCDCON2:
169 s->con[1] = value;
170 s->invalidate = 1;
171 break;
172 case S3C_LCDCON3:
173 s->con[2] = value;
174 s->invalidate = 1;
175 break;
176 case S3C_LCDCON4:
177 s->con[3] = value & 0xffff;
178 break;
179 case S3C_LCDCON5:
180 s->con[4] = value & 0x1fff;
181 s->frm565 = (value >> 11) & 1;
182 s->msb = (value >> 12) & 1;
183 s->invalidatep = 1;
184 s->invalidate = 1;
185 break;
186 case S3C_LCDSADDR1:
187 s->saddr[0] = value;
188 s->invalidate = 1;
189 break;
190 case S3C_LCDSADDR2:
191 s->saddr[1] = value;
192 s->invalidate = 1;
193 break;
194 case S3C_LCDSADDR3:
195 s->saddr[2] = value;
196 s->invalidate = 1;
197 break;
198 case S3C_REDLUT:
199 s->r = value;
200 s->invalidatep = 1;
201 s->invalidate = 1;
202 break;
203 case S3C_GREENLUT:
204 s->g = value;
205 s->invalidatep = 1;
206 s->invalidate = 1;
207 break;
208 case S3C_BLUELUT:
209 s->b = value;
210 s->invalidatep = 1;
211 s->invalidate = 1;
212 break;
213 case S3C_DITHMODE:
214 s->dithmode = value;
215 break;
216 case S3C_TPAL:
217 s->tpal = value;
218 s->invalidatep = 1;
219 s->invalidate = 1;
220 break;
221 case S3C_LCDINTPND:
222 s->intpnd = value & 3;
223 break;
224 case S3C_LCDSRCPND:
225 s->srcpnd = value & 3;
226 break;
227 case S3C_LCDINTMSK:
228 s->intmsk = value & 7;
229 s3c_lcd_update(s);
230 break;
231 case S3C_LPCSEL:
232 s->lpcsel = (value & 3) | 4;
233 if (value & 1)
234 printf("%s: attempt to enable LPC3600\n", __FUNCTION__);
235 break;
236 case S3C_PALETTE ... S3C_PALETTEEND:
237 /* XXX assuming 16bit access */
238 s->raw_pal[(addr - S3C_PALETTE) >> 1] = value;
239 break;
240 default:
241 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
245 static CPUReadMemoryFunc *s3c_lcd_readfn[] = {
246 s3c_lcd_read,
247 s3c_lcd_read,
248 s3c_lcd_read,
251 static CPUWriteMemoryFunc *s3c_lcd_writefn[] = {
252 s3c_lcd_write,
253 s3c_lcd_write,
254 s3c_lcd_write,
257 static inline void s3c_lcd_resize(struct s3c_lcd_state_s *s)
259 int new_width, new_height;
260 new_height = ((s->con[1] >> 14) & 0x3ff) + 1;
261 new_width = ((s->con[2] >> 8) & 0x7ff) + 1;
262 if (s->width != new_width || s->height != new_height) {
263 s->width = new_width;
264 s->height = new_height;
265 // dpy_resize(s->ds, s->width, s->height);
266 qemu_console_resize(s->ds, s->width, s->height);
267 s->invalidate = 1;
271 static inline
272 uint32_t s3c_rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
274 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
277 static inline
278 uint32_t s3c_rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
280 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
283 static inline
284 uint32_t s3c_rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
286 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
289 static inline
290 uint32_t s3c_rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
292 return (r << 16) | (g << 8) | b;
295 static inline
296 uint32_t s3c_rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
298 return (r << 16) | (g << 8) | b;
301 static inline uint32_t s3c_rgb(struct s3c_lcd_state_s *s,
302 unsigned int r, unsigned int g, unsigned b)
304 switch (ds_get_bits_per_pixel(s->ds)) {
305 case 8:
306 return s3c_rgb_to_pixel32(r << 2, g << 2, b << 2);
307 case 15:
308 return s3c_rgb_to_pixel15(r << 2, g << 2, b << 2);
309 case 16:
310 return s3c_rgb_to_pixel16(r << 2, g << 2, b << 2);
311 case 24:
312 return s3c_rgb_to_pixel24(r << 2, g << 2, b << 2);
313 case 32:
314 return s3c_rgb_to_pixel32(r << 2, g << 2, b << 2);
315 default:
316 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
317 exit(1);
321 static void s3c_lcd_palette_load(struct s3c_lcd_state_s *s)
323 int i, n;
324 switch (s->bpp) {
325 case 0:
326 case 8:
327 n = 2;
328 s->src_width = s->width >> 3;
329 s->fn = s->line_fn[0];
330 break;
331 case 1:
332 case 9:
333 n = 4;
334 s->src_width = s->width >> 2;
335 s->fn = s->line_fn[1];
336 break;
337 case 2:
338 case 10:
339 n = 16;
340 s->src_width = s->width >> 1;
341 s->fn = s->line_fn[2];
342 break;
343 case 3:
344 case 11:
345 n = 256;
346 s->src_width = s->width >> 0;
347 s->fn = s->line_fn[3];
348 break;
349 case 6:
350 s->src_width = (s->width * 3) >> 1;
351 s->fn = s->line_fn[4];
352 return;
353 case 12:
354 s->src_width = s->width << 1;
355 if (s->frm565)
356 s->fn = s->line_fn[5];
357 else
358 s->fn = s->line_fn[6];
359 return;
360 case 13:
361 s->src_width = s->width << 2;
362 s->fn = s->line_fn[7];
363 return;
364 default:
365 return;
367 if (s->bpp & 8) {
368 for (i = 0; i < n; i ++)
369 if (s->frm565)
370 s->palette[i] = s3c_rgb(s,
371 (s->raw_pal[i] >> 10) & 0x3e,
372 (s->raw_pal[i] >> 5) & 0x3f,
373 (s->raw_pal[i] << 1) & 0x3e);
374 else
375 s->palette[i] = s3c_rgb(s,
376 ((s->raw_pal[i] >> 10) & 0x3e) | (s->raw_pal[i] & 1),
377 ((s->raw_pal[i] >> 6) & 0x3e) | (s->raw_pal[i] & 1),
378 s->raw_pal[i] & 0x3f);
379 } else {
380 for (i = 0; i < n; i ++)
381 if (n < 256)
382 s->palette[i] = s3c_rgb(s,
383 ((s->r >> (i * 4)) & 0xf) << 2,
384 ((s->g >> (i * 4)) & 0xf) << 2,
385 ((s->b >> (i * 4)) & 0xf) << 2);
386 else
387 s->palette[i] = s3c_rgb(s,
388 ((s->r >> (((i >> 5) & 7) * 4)) & 0xf) << 2,
389 ((s->g >> (((i >> 2) & 7) * 4)) & 0xf) << 2,
390 ((s->b >> ((i & 3) * 4)) & 0xf) << 2);
394 static void s3c_update_display(void *opaque)
396 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
397 int src_width, dest_width;
398 ram_addr_t addr;
399 int first, last;
401 s3c_lcd_resize(s);
403 if (s->invalidatep) {
404 s3c_lcd_palette_load(s);
405 s->invalidatep = 0;
408 addr = s->saddr[0]<<1;
410 src_width = s->src_width;
411 dest_width = s->width * s->dest_width;
413 first = 0;
414 framebuffer_update_display(s->ds,
415 addr, s->width, s->height,
416 src_width, dest_width, s->dest_width,
417 s->invalidate,
418 s->fn, s->palette,
419 &first, &last);
421 s->srcpnd |= (1 << 1); /* INT_FrSyn */
422 s3c_lcd_update(s);
423 if (first >= 0) {
424 dpy_update(s->ds, 0, first, s->width, last - first + 1);
426 s->invalidate = 0;
429 static void s3c_invalidate_display(void *opaque)
431 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
432 s->invalidate = 1;
435 static void s3c_screen_dump(void *opaque, const char *filename)
437 /* TODO */
440 #define BITS 8
441 #include "s3c24xx_template.h"
442 #define BITS 15
443 #include "s3c24xx_template.h"
444 #define BITS 16
445 #include "s3c24xx_template.h"
446 #define BITS 24
447 #include "s3c24xx_template.h"
448 #define BITS 32
449 #include "s3c24xx_template.h"
451 static void s3c_lcd_save(QEMUFile *f, void *opaque)
453 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
454 int i;
455 for (i = 0; i < 5; i ++)
456 qemu_put_be32s(f, &s->con[i]);
457 for (i = 0; i < 3; i ++)
458 qemu_put_be32s(f, &s->saddr[i]);
459 qemu_put_be32s(f, &s->r);
460 qemu_put_be32s(f, &s->g);
461 qemu_put_be16s(f, &s->b);
462 qemu_put_be32s(f, &s->dithmode);
463 qemu_put_be32s(f, &s->tpal);
464 qemu_put_8s(f, &s->intpnd);
465 qemu_put_8s(f, &s->srcpnd);
466 qemu_put_8s(f, &s->intmsk);
467 qemu_put_8s(f, &s->lpcsel);
468 for (i = 0; i < 0x100; i ++)
469 qemu_put_be16s(f, &s->raw_pal[i]);
472 static int s3c_lcd_load(QEMUFile *f, void *opaque, int version_id)
474 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
475 int i;
476 for (i = 0; i < 5; i ++)
477 qemu_get_be32s(f, &s->con[i]);
478 for (i = 0; i < 3; i ++)
479 qemu_get_be32s(f, &s->saddr[i]);
480 qemu_get_be32s(f, &s->r);
481 qemu_get_be32s(f, &s->g);
482 qemu_get_be16s(f, &s->b);
483 qemu_get_be32s(f, &s->dithmode);
484 qemu_get_be32s(f, &s->tpal);
485 qemu_get_8s(f, &s->intpnd);
486 qemu_get_8s(f, &s->srcpnd);
487 qemu_get_8s(f, &s->intmsk);
488 qemu_get_8s(f, &s->lpcsel);
490 s->invalidate = 1;
491 s->invalidatep = 1;
492 s->width = -1;
493 s->height = -1;
494 s->bpp = (s->con[0] >> 1) & 0xf;
495 s->enable = s->con[0] & 1;
496 s->msb = (s->con[4] >> 12) & 1;
497 s->frm565 = (s->con[4] >> 11) & 1;
499 for (i = 0; i < 0x100; i ++)
500 qemu_get_be16s(f, &s->raw_pal[i]);
502 return 0;
505 struct s3c_lcd_state_s *s3c_lcd_init(target_phys_addr_t base,
506 qemu_irq irq)
508 int iomemtype;
509 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *)
510 qemu_mallocz(sizeof(struct s3c_lcd_state_s));
512 s->base = base;
513 s->irq = irq;
515 s3c_lcd_reset(s);
517 s->ds = graphic_console_init(
518 s3c_update_display,
519 s3c_invalidate_display,
520 s3c_screen_dump, NULL, s);
522 iomemtype = cpu_register_io_memory(0, s3c_lcd_readfn,
523 s3c_lcd_writefn, s);
524 cpu_register_physical_memory(s->base, 0xffffff, iomemtype);
526 register_savevm("s3c24xx_lcd", 0, 0, s3c_lcd_save, s3c_lcd_load, s);
528 switch (ds_get_bits_per_pixel(s->ds)) {
529 case 0:
530 s->dest_width = 0;
531 break;
532 case 8:
533 s->line_fn = s3c_draw_fn_8;
534 s->dest_width = 1;
535 break;
536 case 15:
537 s->line_fn = s3c_draw_fn_15;
538 s->dest_width = 2;
539 break;
540 case 16:
541 s->line_fn = s3c_draw_fn_16;
542 s->dest_width = 2;
543 break;
544 case 24:
545 s->line_fn = s3c_draw_fn_24;
546 s->dest_width = 3;
547 break;
548 case 32:
549 s->line_fn = s3c_draw_fn_32;
550 s->dest_width = 4;
551 break;
552 default:
553 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
554 exit(1);
556 return s;