[S3C] Import S3C support from OpenMoko
[qemu/mini2440.git] / hw / s3c24xx_lcd.c
blob8926aeab6529a9a4d7df181e513edadf1d050568
1 /*
2 * Samsung S3C24xx series LCD controller.
4 * Copyright (c) 2007 OpenMoko, Inc.
5 * Author: Andrzej Zaborowski <andrew@openedhand.com>
7 * This code is licenced under the GNU GPL v2.
8 */
10 #include "s3c.h"
11 #include "hw.h"
12 #include "console.h"
14 typedef void (*s3c_drawfn_t)(uint32_t *, uint8_t *, const uint8_t *, int, int);
16 struct s3c_lcd_state_s {
17 target_phys_addr_t base;
18 void *irq;
19 DisplayState *ds;
20 s3c_drawfn_t *line_fn;
22 uint32_t con[5];
23 uint32_t saddr[3];
24 uint32_t r;
25 uint32_t g;
26 uint16_t b;
27 uint32_t dithmode;
28 uint32_t tpal;
29 uint8_t intpnd;
30 uint8_t srcpnd;
31 uint8_t intmsk;
32 uint8_t lpcsel;
34 uint16_t raw_pal[0x100];
36 int width;
37 int height;
38 int bpp;
39 int enable;
40 int msb;
41 int frm565;
42 void *fb;
43 uint32_t palette[0x100];
44 int invalidate;
45 int invalidatep;
46 int src_width;
47 int dest_width;
48 s3c_drawfn_t 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;
109 addr -= s->base;
111 switch (addr) {
112 case S3C_LCDCON1:
113 return s->con[0]; /* XXX Return random LINECNT? */
114 case S3C_LCDCON2:
115 return s->con[1];
116 case S3C_LCDCON3:
117 return s->con[2];
118 case S3C_LCDCON4:
119 return s->con[3];
120 case S3C_LCDCON5:
121 return s->con[4]; /* XXX Return random STATUS? */
122 case S3C_LCDSADDR1:
123 return s->saddr[0];
124 case S3C_LCDSADDR2:
125 return s->saddr[1];
126 case S3C_LCDSADDR3:
127 return s->saddr[2];
128 case S3C_REDLUT:
129 return s->r;
130 case S3C_GREENLUT:
131 return s->g;
132 case S3C_BLUELUT:
133 return s->b;
134 case S3C_DITHMODE:
135 return s->dithmode;
136 case S3C_TPAL:
137 return s->tpal;
138 case S3C_LCDINTPND:
139 return s->intpnd;
140 case S3C_LCDSRCPND:
141 return s->srcpnd;
142 case S3C_LCDINTMSK:
143 return s->intmsk;
144 case S3C_LPCSEL:
145 return s->lpcsel;
146 case S3C_PALETTE ... S3C_PALETTEEND:
147 /* XXX assuming 16bit access */
148 return s->raw_pal[(addr - S3C_PALETTE) >> 1];
149 default:
150 printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr);
151 break;
153 return 0;
156 static void s3c_lcd_write(void *opaque, target_phys_addr_t addr,
157 uint32_t value)
159 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
160 addr -= s->base;
162 switch (addr) {
163 case S3C_LCDCON1:
164 s->con[0] = value & 0x0003ffff;
165 s->enable = value & 1;
166 s->bpp = (value >> 1) & 0xf;
167 s->invalidate = 1;
168 s->invalidatep = 1;
169 break;
170 case S3C_LCDCON2:
171 s->con[1] = value;
172 s->invalidate = 1;
173 break;
174 case S3C_LCDCON3:
175 s->con[2] = value;
176 s->invalidate = 1;
177 break;
178 case S3C_LCDCON4:
179 s->con[3] = value & 0xffff;
180 break;
181 case S3C_LCDCON5:
182 s->con[4] = value & 0x1fff;
183 s->frm565 = (value >> 11) & 1;
184 s->msb = (value >> 12) & 1;
185 s->invalidatep = 1;
186 s->invalidate = 1;
187 break;
188 case S3C_LCDSADDR1:
189 s->saddr[0] = value;
190 s->fb = phys_ram_base +
191 (((s->saddr[0] << 1) & 0x7ffffffe) - S3C_RAM_BASE);
192 s->invalidate = 1;
193 break;
194 case S3C_LCDSADDR2:
195 s->saddr[1] = value;
196 s->invalidate = 1;
197 break;
198 case S3C_LCDSADDR3:
199 s->saddr[2] = value;
200 s->invalidate = 1;
201 break;
202 case S3C_REDLUT:
203 s->r = value;
204 s->invalidatep = 1;
205 s->invalidate = 1;
206 break;
207 case S3C_GREENLUT:
208 s->g = value;
209 s->invalidatep = 1;
210 s->invalidate = 1;
211 break;
212 case S3C_BLUELUT:
213 s->b = value;
214 s->invalidatep = 1;
215 s->invalidate = 1;
216 break;
217 case S3C_DITHMODE:
218 s->dithmode = value;
219 break;
220 case S3C_TPAL:
221 s->tpal = value;
222 s->invalidatep = 1;
223 s->invalidate = 1;
224 break;
225 case S3C_LCDINTPND:
226 s->intpnd = value & 3;
227 break;
228 case S3C_LCDSRCPND:
229 s->srcpnd = value & 3;
230 break;
231 case S3C_LCDINTMSK:
232 s->intmsk = value & 7;
233 s3c_lcd_update(s);
234 break;
235 case S3C_LPCSEL:
236 s->lpcsel = (value & 3) | 4;
237 if (value & 1)
238 printf("%s: attempt to enable LPC3600\n", __FUNCTION__);
239 break;
240 case S3C_PALETTE ... S3C_PALETTEEND:
241 /* XXX assuming 16bit access */
242 s->raw_pal[(addr - S3C_PALETTE) >> 1] = value;
243 break;
244 default:
245 printf("%s: Bad register 0x%lx\n", __FUNCTION__, addr);
249 static CPUReadMemoryFunc *s3c_lcd_readfn[] = {
250 s3c_lcd_read,
251 s3c_lcd_read,
252 s3c_lcd_read,
255 static CPUWriteMemoryFunc *s3c_lcd_writefn[] = {
256 s3c_lcd_write,
257 s3c_lcd_write,
258 s3c_lcd_write,
261 static inline void s3c_lcd_resize(struct s3c_lcd_state_s *s)
263 int new_width, new_height;
264 new_height = ((s->con[1] >> 14) & 0x3ff) + 1;
265 new_width = ((s->con[2] >> 8) & 0x7ff) + 1;
266 if (s->width != new_width || s->height != new_height) {
267 s->width = new_width;
268 s->height = new_height;
269 dpy_resize(s->ds, s->width, s->height);
270 s->invalidate = 1;
274 static inline
275 uint32_t s3c_rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
277 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
280 static inline
281 uint32_t s3c_rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
283 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
286 static inline
287 uint32_t s3c_rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
289 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
292 static inline
293 uint32_t s3c_rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
295 return (r << 16) | (g << 8) | b;
298 static inline
299 uint32_t s3c_rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
301 return (r << 16) | (g << 8) | b;
304 static inline uint32_t s3c_rgb(struct s3c_lcd_state_s *s,
305 unsigned int r, unsigned int g, unsigned b)
307 switch (s->ds->depth) {
308 case 8:
309 return s3c_rgb_to_pixel32(r << 2, g << 2, b << 2);
310 case 15:
311 return s3c_rgb_to_pixel15(r << 2, g << 2, b << 2);
312 case 16:
313 return s3c_rgb_to_pixel16(r << 2, g << 2, b << 2);
314 case 24:
315 return s3c_rgb_to_pixel24(r << 2, g << 2, b << 2);
316 case 32:
317 return s3c_rgb_to_pixel32(r << 2, g << 2, b << 2);
318 default:
319 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
320 exit(1);
324 static void s3c_lcd_palette_load(struct s3c_lcd_state_s *s)
326 int i, n;
327 switch (s->bpp) {
328 case 0:
329 case 8:
330 n = 2;
331 s->src_width = s->width >> 3;
332 s->fn = s->line_fn[0];
333 break;
334 case 1:
335 case 9:
336 n = 4;
337 s->src_width = s->width >> 2;
338 s->fn = s->line_fn[1];
339 break;
340 case 2:
341 case 10:
342 n = 16;
343 s->src_width = s->width >> 1;
344 s->fn = s->line_fn[2];
345 break;
346 case 3:
347 case 11:
348 n = 256;
349 s->src_width = s->width >> 0;
350 s->fn = s->line_fn[3];
351 break;
352 case 6:
353 s->src_width = (s->width * 3) >> 1;
354 s->fn = s->line_fn[4];
355 return;
356 case 12:
357 s->src_width = s->width << 1;
358 if (s->frm565)
359 s->fn = s->line_fn[5];
360 else
361 s->fn = s->line_fn[6];
362 return;
363 case 13:
364 s->src_width = s->width << 2;
365 s->fn = s->line_fn[7];
366 return;
367 default:
368 return;
370 if (s->bpp & 8) {
371 for (i = 0; i < n; i ++)
372 if (s->frm565)
373 s->palette[i] = s3c_rgb(s,
374 (s->raw_pal[i] >> 10) & 0x3e,
375 (s->raw_pal[i] >> 5) & 0x3f,
376 (s->raw_pal[i] << 1) & 0x3e);
377 else
378 s->palette[i] = s3c_rgb(s,
379 ((s->raw_pal[i] >> 10) & 0x3e) | (s->raw_pal[i] & 1),
380 ((s->raw_pal[i] >> 6) & 0x3e) | (s->raw_pal[i] & 1),
381 s->raw_pal[i] & 0x3f);
382 } else {
383 for (i = 0; i < n; i ++)
384 if (n < 256)
385 s->palette[i] = s3c_rgb(s,
386 ((s->r >> (i * 4)) & 0xf) << 2,
387 ((s->g >> (i * 4)) & 0xf) << 2,
388 ((s->b >> (i * 4)) & 0xf) << 2);
389 else
390 s->palette[i] = s3c_rgb(s,
391 ((s->r >> (((i >> 5) & 7) * 4)) & 0xf) << 2,
392 ((s->g >> (((i >> 2) & 7) * 4)) & 0xf) << 2,
393 ((s->b >> ((i & 3) * 4)) & 0xf) << 2);
397 static void s3c_update_display(void *opaque)
399 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
400 int y, src_width, dest_width, dirty[2], miny, maxy;
401 ram_addr_t x, addr, new_addr, start, end;
402 uint8_t *src, *dest;
403 if (!s->enable || !s->dest_width)
404 return;
406 s3c_lcd_resize(s);
408 if (s->invalidatep) {
409 s3c_lcd_palette_load(s);
410 s->invalidatep = 0;
413 src = s->fb;
414 src_width = s->src_width;
416 dest = s->ds->data;
417 dest_width = s->width * s->dest_width;
419 addr = (ram_addr_t) (s->fb - (void *) phys_ram_base);
420 start = addr + s->height * src_width;
421 end = addr;
422 dirty[0] = dirty[1] = cpu_physical_memory_get_dirty(start, VGA_DIRTY_FLAG);
423 miny = s->height;
424 maxy = 0;
425 for (y = 0; y < s->height; y ++) {
426 new_addr = addr + src_width;
427 for (x = addr + TARGET_PAGE_SIZE; x < new_addr;
428 x += TARGET_PAGE_SIZE) {
429 dirty[1] = cpu_physical_memory_get_dirty(x, VGA_DIRTY_FLAG);
430 dirty[0] |= dirty[1];
432 if (dirty[0] || s->invalidate) {
433 s->fn(s->palette, dest, src, s->width, s->dest_width);
434 maxy = y;
435 end = new_addr;
436 if (y < miny) {
437 miny = y;
438 start = addr;
441 addr = new_addr;
442 dirty[0] = dirty[1];
443 src += src_width;
444 dest += dest_width;
447 s->invalidate = 0;
448 if (end > start)
449 cpu_physical_memory_reset_dirty(start, end, VGA_DIRTY_FLAG);
450 s->srcpnd |= (1 << 1); /* INT_FrSyn */
451 s3c_lcd_update(s);
452 dpy_update(s->ds, 0, miny, s->width, maxy);
455 static void s3c_invalidate_display(void *opaque)
457 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
458 s->invalidate = 1;
461 static void s3c_screen_dump(void *opaque, const char *filename)
463 /* TODO */
466 #define BITS 8
467 #include "s3c24xx_template.h"
468 #define BITS 15
469 #include "s3c24xx_template.h"
470 #define BITS 16
471 #include "s3c24xx_template.h"
472 #define BITS 24
473 #include "s3c24xx_template.h"
474 #define BITS 32
475 #include "s3c24xx_template.h"
477 static void s3c_lcd_save(QEMUFile *f, void *opaque)
479 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
480 int i;
481 for (i = 0; i < 5; i ++)
482 qemu_put_be32s(f, &s->con[i]);
483 for (i = 0; i < 3; i ++)
484 qemu_put_be32s(f, &s->saddr[i]);
485 qemu_put_be32s(f, &s->r);
486 qemu_put_be32s(f, &s->g);
487 qemu_put_be16s(f, &s->b);
488 qemu_put_be32s(f, &s->dithmode);
489 qemu_put_be32s(f, &s->tpal);
490 qemu_put_8s(f, &s->intpnd);
491 qemu_put_8s(f, &s->srcpnd);
492 qemu_put_8s(f, &s->intmsk);
493 qemu_put_8s(f, &s->lpcsel);
494 for (i = 0; i < 0x100; i ++)
495 qemu_put_be16s(f, &s->raw_pal[i]);
498 static int s3c_lcd_load(QEMUFile *f, void *opaque, int version_id)
500 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
501 int i;
502 for (i = 0; i < 5; i ++)
503 qemu_get_be32s(f, &s->con[i]);
504 for (i = 0; i < 3; i ++)
505 qemu_get_be32s(f, &s->saddr[i]);
506 qemu_get_be32s(f, &s->r);
507 qemu_get_be32s(f, &s->g);
508 qemu_get_be16s(f, &s->b);
509 qemu_get_be32s(f, &s->dithmode);
510 qemu_get_be32s(f, &s->tpal);
511 qemu_get_8s(f, &s->intpnd);
512 qemu_get_8s(f, &s->srcpnd);
513 qemu_get_8s(f, &s->intmsk);
514 qemu_get_8s(f, &s->lpcsel);
516 s->invalidate = 1;
517 s->invalidatep = 1;
518 s->width = -1;
519 s->height = -1;
520 s->bpp = (s->con[0] >> 1) & 0xf;
521 s->enable = s->con[0] & 1;
522 s->msb = (s->con[4] >> 12) & 1;
523 s->frm565 = (s->con[4] >> 11) & 1;
524 s->fb = phys_ram_base + (((s->saddr[0] << 1) & 0x7ffffffe) - S3C_RAM_BASE);
526 for (i = 0; i < 0x100; i ++)
527 qemu_get_be16s(f, &s->raw_pal[i]);
529 return 0;
532 struct s3c_lcd_state_s *s3c_lcd_init(target_phys_addr_t base, DisplayState *ds,
533 qemu_irq irq)
535 int iomemtype;
536 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *)
537 qemu_mallocz(sizeof(struct s3c_lcd_state_s));
539 s->base = base;
540 s->irq = irq;
541 s->ds = ds;
543 s3c_lcd_reset(s);
545 graphic_console_init(ds, s3c_update_display,
546 s3c_invalidate_display, s3c_screen_dump, s);
548 iomemtype = cpu_register_io_memory(0, s3c_lcd_readfn,
549 s3c_lcd_writefn, s);
550 cpu_register_physical_memory(s->base, 0xffffff, iomemtype);
552 register_savevm("s3c24xx_lcd", 0, 0, s3c_lcd_save, s3c_lcd_load, s);
554 switch (s->ds->depth) {
555 case 0:
556 s->dest_width = 0;
557 break;
558 case 8:
559 s->line_fn = s3c_draw_fn_8;
560 s->dest_width = 1;
561 break;
562 case 15:
563 s->line_fn = s3c_draw_fn_15;
564 s->dest_width = 2;
565 break;
566 case 16:
567 s->line_fn = s3c_draw_fn_16;
568 s->dest_width = 2;
569 break;
570 case 24:
571 s->line_fn = s3c_draw_fn_24;
572 s->dest_width = 3;
573 break;
574 case 32:
575 s->line_fn = s3c_draw_fn_32;
576 s->dest_width = 4;
577 break;
578 default:
579 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
580 exit(1);
582 return s;