Tizen 2.1 base
[sdk/emulator/qemu.git] / hw / s5pc1xx_lcd.c
1 /*
2  * S5PC1XX LCD Controller
3  *
4  * Copyright (c) 2010 Samsung Electronics.
5  * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
6  */
7
8 /*
9  * Known issues:
10  *    multiple windows blending - implemented but not tested
11  *    shadow registers - not implemented
12  *    i80 indirect interface - not implemented
13  *    dithering - not implemented
14  *    RTQoS - not implemented
15  */
16
17 #include "console.h"
18 #include "pixel_ops.h"
19 #include "s5pc1xx.h"
20 #include "sysbus.h"
21
22
23 #define BISWP   0x8
24 #define BYSWP   0x4
25 #define HWSWP   0x2
26 #define WSWP    0x1
27
28
29 typedef struct {
30     uint8_t r, g, b;
31     uint32_t a;
32 } rgba;
33
34 struct DrawConfig;
35
36 typedef void pixel_to_rgb_func(uint32_t pixel, rgba *p);
37 typedef void draw_line_func(struct DrawConfig *cfg, uint8_t *src,
38                             uint8_t *dst, uint8_t *ifb);
39 typedef uint32_t coef_func(const struct DrawConfig *cfg, rgba pa, rgba pb);
40
41 typedef struct DrawConfig {
42     pixel_to_rgb_func *pixel_to_rgb;
43     draw_line_func *draw_line;
44     int (*put_pixel)(rgba p, uint8_t *pixel);
45     int (*get_pixel)(uint8_t *src, rgba *p);
46     void (*blend)(struct DrawConfig *cfg, rgba p_old, rgba p_new, rgba *p);
47     coef_func *coef_p, *coef_q, *coef_a, *coef_b;
48     uint8_t is_palletized;
49     uint32_t bg_alpha[2], fg_alpha[2];
50     uint32_t color_key, color_mask, color_ctl;
51     uint8_t fg_alpha_pix, bg_alpha_pix;
52     int width;
53     int bpp;
54     uint32_t *palette;
55     uint8_t swap;
56     uint8_t fg_pixel_blending, bg_pixel_blending;
57     uint8_t fg_alpha_sel, bg_alpha_sel;
58 } DrawConfig;
59
60 typedef struct S5pc1xxLcdWindow {
61     uint32_t wincon;
62     uint32_t vidosd[4];
63     uint32_t buf_start[2];
64     uint32_t buf_end[2];
65     uint32_t buf_size;
66     uint32_t keycon[2];
67     uint32_t winmap;
68     uint32_t vidw_alpha[2];
69     uint32_t blendeq;
70     uint32_t palette[256];
71 } S5pc1xxLcdWindow;
72
73 typedef struct {
74     SysBusDevice busdev;
75
76     uint32_t shadowcon;
77     uint32_t vidcon[3];
78     uint32_t prtcon;
79     uint32_t vidtcon[3];
80     uint32_t vp1tcon[2];
81     uint32_t vidintcon[2];
82     uint32_t dithcon;
83     uint32_t wpalcon[2];
84     uint32_t trigcon;
85     uint32_t ituifcon;
86     uint32_t i80ifcon[4];
87     uint32_t ldi_cmdcon[2];
88     uint32_t sifccon[3];
89     uint32_t blendcon;
90     uint32_t ldi_cmd[12];
91     uint32_t dualrgb;
92
93     S5pc1xxLcdWindow window[5];
94     uint8_t *ifb;
95     uint8_t *valid_line;
96     uint8_t *valid_line_prev;
97     DisplayState *console;
98     uint8_t invalidate;
99     qemu_irq irq[3];
100 } S5pc1xxLcdState;
101
102
103 /* Palette/pixel to RGB conversion */
104
105 #define DEF_PIXEL_TO_RGB(N,R,G,B,A) \
106 static void N(uint32_t pixel, rgba *p) \
107 { \
108     p->b = (pixel & ((1 << (B)) - 1)) << (8 - (B)); \
109     pixel >>= (B); \
110     p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)); \
111     pixel >>= (G); \
112     p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)); \
113     pixel >>= (R); \
114     if (1 == (A)) { \
115         p->a = pixel & 1; \
116     } else if (8 == (A)) { \
117         p->a = pixel & 0xFF; \
118         p->a = (p->a << 16) | (p->a << 8) | p->a; \
119     } else { \
120         p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)); \
121     } \
122 }
123
124 DEF_PIXEL_TO_RGB(pixel_a232_to_rgb, 2, 3, 2, 1)
125 DEF_PIXEL_TO_RGB(pixel_a444_to_rgb, 4, 4, 4, 1)
126 DEF_PIXEL_TO_RGB(pixel_4444_to_rgb, 4, 4, 4, 4)
127 DEF_PIXEL_TO_RGB(pixel_565_to_rgb,  5, 6, 5, 0)
128 DEF_PIXEL_TO_RGB(pixel_a555_to_rgb, 5, 5, 5, 1)
129 DEF_PIXEL_TO_RGB(pixel_555_to_rgb,  5, 5, 5, 0)
130 DEF_PIXEL_TO_RGB(pixel_666_to_rgb,  6, 6, 6, 0)
131 DEF_PIXEL_TO_RGB(pixel_a666_to_rgb, 6, 6, 6, 1)
132 DEF_PIXEL_TO_RGB(pixel_a665_to_rgb, 6, 6, 5, 1)
133 DEF_PIXEL_TO_RGB(pixel_888_to_rgb,  8, 8, 8, 0)
134 DEF_PIXEL_TO_RGB(pixel_a888_to_rgb, 8, 8, 8, 1)
135 DEF_PIXEL_TO_RGB(pixel_a887_to_rgb, 8, 8, 7, 1)
136 DEF_PIXEL_TO_RGB(pixel_8888_to_rgb, 8, 8, 8, 8)
137
138 /* Special case for (5+1,5+1,5+1) mode */
139 static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
140 {
141     uint8_t u = (pixel >> 15) & 1;
142     p->b = (((pixel & 0x1F) << 1) | u) << 2;
143     pixel >>= 5;
144     p->g = (((pixel & 0x3F) << 1) | u) << 2;
145     pixel >>= 6;
146     p->r = (((pixel & 0x1F) << 1) | u) << 2;
147 }
148
149
150 /* Write RGB to QEMU's GraphicConsole framebuffer */
151
152 static int put_pixel8(rgba p, uint8_t *d)
153 {
154     uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
155     *(uint8_t *)d = pixel;
156     return 1;
157 }
158
159 static int put_pixel15(rgba p, uint8_t *d)
160 {
161     uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
162     *(uint16_t *)d = pixel;
163     return 2;
164 }
165
166 static int put_pixel16(rgba p, uint8_t *d)
167 {
168     uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
169     *(uint16_t *)d = pixel;
170     return 2;
171 }
172
173 static int put_pixel24(rgba p, uint8_t *d)
174 {
175     uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
176     *(uint8_t *)d++ = (pixel >>  0) & 0xFF;
177     *(uint8_t *)d++ = (pixel >>  8) & 0xFF;
178     *(uint8_t *)d++ = (pixel >> 16) & 0xFF;
179     return 3;
180 }
181
182 static int put_pixel32(rgba p, uint8_t *d)
183 {
184     uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
185     *(uint32_t *)d = pixel;
186     return 4;
187 }
188
189
190 /* Put/get pixel to/from internal LCD Controller framebuffer */
191
192 static int put_rgba(rgba p, uint8_t *d)
193 {
194     *(uint8_t *)d++ = p.r;
195     *(uint8_t *)d++ = p.g;
196     *(uint8_t *)d++ = p.b;
197     *(uint32_t *)d = p.a;
198     return 7;
199 }
200
201 static int get_rgba(uint8_t *s, rgba *p)
202 {
203     p->r = *(uint8_t *)s++;
204     p->g = *(uint8_t *)s++;
205     p->b = *(uint8_t *)s++;
206     p->a = *(uint32_t *)s;
207     return 7;
208 }
209
210
211 /* Perform byte/halfword/word swap of data accrding to config */
212
213 static inline uint64_t swap_data(const DrawConfig *cfg, uint64_t x)
214 {
215     int i;
216     uint64_t res;
217
218     return x;
219
220     if (cfg->swap & BISWP) {
221         res = 0;
222         for (i = 0; i < 64; i++) {
223             if (x & (1ULL << (64 - i))) {
224                 res |= (1ULL << i);
225             }
226         }
227         x = res;
228     }
229     if (cfg->swap & BYSWP) {
230         x = ((x & 0x00000000000000FFULL) << 56) |
231             ((x & 0x000000000000FF00ULL) << 40) |
232             ((x & 0x0000000000FF0000ULL) << 24) |
233             ((x & 0x00000000FF000000ULL) <<  8) |
234             ((x & 0x000000FF00000000ULL) >>  8) |
235             ((x & 0x0000FF0000000000ULL) >> 24) |
236             ((x & 0x00FF000000000000ULL) >> 40) |
237             ((x & 0xFF00000000000000ULL) >> 56);
238     }
239     if (cfg->swap & HWSWP) {
240         x = ((x & 0x000000000000FFFFULL) << 48) |
241             ((x & 0x00000000FFFF0000ULL) << 16) |
242             ((x & 0x0000FFFF00000000ULL) >> 16) |
243             ((x & 0xFFFF000000000000ULL) >> 48);
244     }
245     if (cfg->swap & WSWP) {
246         x = ((x & 0x00000000FFFFFFFFULL) << 32) |
247             ((x & 0xFFFFFFFF00000000ULL) >> 32);
248     }
249     return x;
250 }
251
252
253 /* Coefficient extraction functions */
254
255 static uint32_t coef_zero(const DrawConfig *cfg,
256                           rgba pa, rgba pb)
257 {
258     return 0;
259 }
260
261 static uint32_t coef_one(const DrawConfig *cfg,
262                          rgba pa, rgba pb)
263 {
264     return 0xFFFFFF;
265 }
266
267 static uint32_t coef_alphaa(const DrawConfig *cfg,
268                             rgba pa, rgba pb)
269 {
270     if (!cfg->fg_pixel_blending) {
271         pa.a = cfg->fg_alpha_sel;
272     }
273     if (cfg->fg_alpha_pix) {
274         return pa.a;
275     } else {
276         return cfg->fg_alpha[pa.a];
277     }
278 }
279
280 static uint32_t coef_one_minus_alphaa(const DrawConfig *cfg,
281                                       rgba pa, rgba pb)
282 {
283     if (!cfg->fg_pixel_blending) {
284         pa.a = cfg->fg_alpha_sel;
285     }
286     if (cfg->fg_alpha_pix) {
287         return 0xFFFFFF - pa.a;
288     } else {
289         return 0xFFFFFF - cfg->fg_alpha[pa.a];
290     }
291 }
292
293 static uint32_t coef_alphab(const DrawConfig *cfg,
294                             rgba pa, rgba pb)
295 {
296     if (!cfg->bg_pixel_blending) {
297         pb.a = cfg->bg_alpha_sel;
298     }
299     if (cfg->bg_alpha_pix) {
300         return pb.a;
301     } else {
302         return cfg->bg_alpha[pb.a];
303     }
304 }
305
306 static uint32_t coef_one_minus_alphab(const DrawConfig *cfg,
307                                       rgba pa, rgba pb)
308 {
309     if (!cfg->bg_pixel_blending) {
310         pb.a = cfg->bg_alpha_sel;
311     }
312     if (cfg->bg_alpha_pix) {
313         return 0xFFFFFF - pb.a;
314     } else {
315         return 0xFFFFFF - cfg->bg_alpha[pb.a];
316     }
317 }
318
319 static uint32_t coef_a(const DrawConfig *cfg,
320                        rgba pa, rgba pb)
321 {
322     return (pa.r << 16) | (pa.g << 8) | pa.b;
323 }
324
325 static uint32_t coef_one_minus_a(const DrawConfig *cfg,
326                                  rgba pa, rgba pb)
327 {
328     return 0xFFFFFF - ((pa.r << 16) | (pa.g << 8) | pa.b);
329 }
330
331 static uint32_t coef_b(const DrawConfig *cfg,
332                        rgba pa, rgba pb)
333 {
334     return (pb.r << 16) | (pb.g << 8) | pb.b;
335 }
336
337 static uint32_t coef_one_minus_b(const DrawConfig *cfg,
338                                  rgba pa, rgba pb)
339 {
340     return 0xFFFFFF - ((pb.r << 16) | (pb.g << 8) | pb.b);
341 }
342
343
344 /* Blending functions */
345
346 static void blend_alpha(const DrawConfig *cfg,
347                         rgba p_bg, rgba p_fg, rgba *res)
348 {
349     uint32_t pl, ql, al, bl;
350     uint32_t p, q, a, b, fg, bg, fga, bga;
351
352     pl = cfg->coef_p(cfg, p_fg, p_bg);
353     ql = cfg->coef_q(cfg, p_fg, p_bg);
354     al = cfg->coef_a(cfg, p_fg, p_bg);
355     bl = cfg->coef_b(cfg, p_fg, p_bg);
356     res->a = 0;
357     /* B */
358     p = pl & 0xFF;
359     pl >>= 8;
360     q = ql & 0xFF;
361     ql >>= 8;
362     a = al & 0xFF;
363     al >>= 8;
364     b = bl & 0xFF;
365     bl >>= 8;
366     fg = p_fg.b;
367     bg = p_bg.b;
368     if (cfg->fg_pixel_blending) {
369         if (cfg->fg_alpha_pix) {
370             fga = p_fg.a & 0xFF;
371         } else {
372             fga = cfg->fg_alpha[p_fg.a] & 0xFF;
373         }
374     } else {
375         fga = cfg->fg_alpha[cfg->fg_alpha_sel] & 0xFF;
376     }
377     if (cfg->bg_pixel_blending) {
378         if (cfg->bg_alpha_pix) {
379             bga = p_bg.a & 0xFF;
380         } else {
381             bga = cfg->bg_alpha[p_bg.a] & 0xFF;
382         }
383     } else {
384         bga = cfg->bg_alpha[cfg->bg_alpha_sel] & 0xFF;
385     }
386     bg = (bg * b + fg * a) / 0xFF;
387     if (bg > 0xFF) {
388         res->b = 0xFF;
389     } else {
390         res->b = bg;
391     }
392     bga = (bga * p + fga * q) / 0xFF;
393     if (bga > 0xFF) {
394         res->a |= 0xFF;
395     } else {
396         res->a |= bga;
397     }
398     /* G */
399     p = pl & 0xFF;
400     pl >>= 8;
401     q = ql & 0xFF;
402     ql >>= 8;
403     a = al & 0xFF;
404     al >>= 8;
405     b = bl & 0xFF;
406     bl >>= 8;
407     fg = p_fg.g;
408     bg = p_bg.g;
409     if (cfg->fg_pixel_blending) {
410         if (cfg->fg_alpha_pix) {
411             fga = (p_fg.a >> 8) & 0xFF;
412         } else {
413             fga = (cfg->fg_alpha[p_fg.a] >> 8) & 0xFF;
414         }
415     } else {
416         fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 8) & 0xFF;
417     }
418     if (cfg->bg_pixel_blending) {
419         if (cfg->bg_alpha_pix) {
420             bga = (p_bg.a >> 8) & 0xFF;
421         } else {
422             bga = (cfg->bg_alpha[p_bg.a] >> 8) & 0xFF;
423         }
424     } else {
425         bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 8) & 0xFF;
426     }
427     bg = (bg * b + fg * a) / 0xFF;
428     if (bg > 0xFF) {
429         res->g = 0xFF;
430     } else {
431         res->g = bg;
432     }
433     bga = (bga * p + fga * q) / 0xFF;
434     if (bga > 0xFF) {
435         res->a |= 0xFF << 8;
436     } else {
437         res->a |= bga << 8;
438     }
439     /* R */
440     p = pl & 0xFF;
441     pl >>= 8;
442     q = ql & 0xFF;
443     ql >>= 8;
444     a = al & 0xFF;
445     al >>= 8;
446     b = bl & 0xFF;
447     bl >>= 8;
448     fg = p_fg.r;
449     bg = p_bg.r;
450     if (cfg->fg_pixel_blending) {
451         if (cfg->fg_alpha_pix) {
452             fga = (p_fg.a >> 16) & 0xFF;
453         } else {
454             fga = (cfg->fg_alpha[p_fg.a] >> 16) & 0xFF;
455         }
456     } else {
457         fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 16) & 0xFF;
458     }
459     if (cfg->bg_pixel_blending) {
460         if (cfg->bg_alpha_pix) {
461             bga = (p_bg.a >> 16) & 0xFF;
462         } else {
463             bga = (cfg->bg_alpha[p_bg.a] >> 16) & 0xFF;
464         }
465     } else {
466         bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 16) & 0xFF;
467     }
468     bg = (bg * b + fg * a) / 0xFF;
469     if (bg > 0xFF) {
470         res->r = 0xFF;
471     } else {
472         res->r = bg;
473     }
474     bga = (bga * p + fga * q) / 0xFF;
475     if (bga > 0xFF) {
476         res->a |= 0xFF << 16;
477     } else {
478         res->a |= bga << 16;
479     }
480 }
481
482 static void blend_colorkey(DrawConfig *cfg,
483                            rgba p_bg, rgba p_fg, rgba *p)
484 {
485     uint8_t r, g, b;
486
487     if (cfg->color_ctl & 2) {
488         blend_alpha(cfg, p_bg, p_fg, p);
489         return ;
490     }
491     r = ((cfg->color_key & ~cfg->color_mask) >> 16) & 0xFF;
492     g = ((cfg->color_key & ~cfg->color_mask) >>  8) & 0xFF;
493     b = ((cfg->color_key & ~cfg->color_mask) >>  0) & 0xFF;
494     if (cfg->color_ctl & 1) {
495         if ((p_fg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
496                 (p_fg.g & ~((cfg->color_mask >>  8) & 0xFF)) == g &&
497                 (p_fg.b & ~((cfg->color_mask >>  0) & 0xFF)) == b) {
498             if (cfg->color_ctl & 4) {
499                 p_fg.a = 1;
500                 cfg->fg_pixel_blending = 0;
501                 blend_alpha(cfg, p_bg, p_fg, p);
502             } else {
503                 *p = p_bg;
504             }
505         } else {
506             if (cfg->color_ctl & 4) {
507                 p_fg.a = 0;
508                 cfg->fg_pixel_blending = 0;
509                 blend_alpha(cfg, p_bg, p_fg, p);
510             } else {
511                 *p = p_fg;
512             }
513         }
514     } else {
515         if ((p_bg.r & ~((cfg->color_mask >> 16) & 0xFF)) == r &&
516                 (p_bg.g & ~((cfg->color_mask >>  8) & 0xFF)) == g &&
517                 (p_bg.b & ~((cfg->color_mask >>  0) & 0xFF)) == b) {
518             if (cfg->color_ctl & 4) {
519                 p_fg.a = 1;
520                 cfg->fg_pixel_blending = 0;
521                 blend_alpha(cfg, p_bg, p_fg, p);
522             } else {
523                 *p = p_fg;
524             }
525         } else {
526             if (cfg->color_ctl & 4) {
527                 p_fg.a = 0;
528                 cfg->fg_pixel_blending = 0;
529                 blend_alpha(cfg, p_bg, p_fg, p);
530             } else {
531                 *p = p_bg;
532             }
533         }
534     }
535 }
536
537
538 /* Draw line functions */
539
540 #define DEF_DRAW_LINE(N) \
541 static void glue(draw_line, N)(DrawConfig *cfg, uint8_t *src, \
542                                uint8_t *dst, uint8_t *ifb) \
543 { \
544     rgba p, p_old; \
545     uint64_t data; \
546     int width = cfg->width; \
547     int i; \
548     do { \
549         data = ldq_raw((void *)src); \
550         src += 8; \
551         data = swap_data(cfg, data); \
552         for (i = 0; i < (64 / (N)); i++) { \
553             cfg->pixel_to_rgb(cfg->is_palletized ? \
554                                   cfg->palette[data & ((1ULL << (N)) - 1)] : \
555                                   data & ((1ULL << (N)) - 1), &p); \
556             if (cfg->blend) { \
557                 ifb += cfg->get_pixel(ifb, &p_old); \
558                 cfg->blend(cfg, p_old, p, &p); \
559             } \
560             dst += cfg->put_pixel(p, dst); \
561             data >>= (N); \
562         } \
563         width -= (64 / (N)); \
564     } while (width > 0); \
565 }
566
567 DEF_DRAW_LINE(1)
568 DEF_DRAW_LINE(2)
569 DEF_DRAW_LINE(4)
570 DEF_DRAW_LINE(8)
571 DEF_DRAW_LINE(16)
572 DEF_DRAW_LINE(32)
573
574 static void draw_line_copy(DrawConfig *cfg, uint8_t *src, uint8_t *dst,
575                            uint8_t *ifb)
576 {
577     rgba p;
578     int width = cfg->width;
579
580     do {
581         src += cfg->get_pixel(src, &p);
582         dst += cfg->put_pixel(p, dst);
583         width--;
584     } while (width > 0);
585 }
586
587
588 /* LCD Functions */
589
590 static void s5pc1xx_lcd_update_irq(S5pc1xxLcdState *s)
591 {
592     if (!(s->vidintcon[0] & 1)) {
593         qemu_irq_lower(s->irq[0]);
594         qemu_irq_lower(s->irq[1]);
595         qemu_irq_lower(s->irq[2]);
596         return;
597     }
598     if ((s->vidintcon[0] & 2) && (s->vidintcon[1] & 1)) {
599         qemu_irq_raise(s->irq[0]);
600     } else {
601         qemu_irq_lower(s->irq[0]);
602     }
603     if ((s->vidintcon[0] & (1 << 12)) && (s->vidintcon[1] & 2)) {
604         qemu_irq_raise(s->irq[1]);
605     } else {
606         qemu_irq_lower(s->irq[1]);
607     }
608     if ((s->vidintcon[0] & (1 << 17)) && (s->vidintcon[1] & 4)) {
609         qemu_irq_raise(s->irq[2]);
610     } else {
611         qemu_irq_lower(s->irq[2]);
612     }
613 }
614
615 static void s5pc1xx_lcd_write(void *opaque, target_phys_addr_t offset,
616                               uint32_t val)
617 {
618     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
619     int w, i;
620
621     if (offset & 3) {
622         hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n", offset);
623     }
624
625     switch (offset) {
626         case 0x000 ... 0x008:
627             s->vidcon[(offset - 0x000) >> 2] = val;
628             break;
629         case 0x00C:
630             s->prtcon = val;
631             break;
632         case 0x010 ... 0x018:
633             s->vidtcon[(offset - 0x010) >> 2] = val;
634             break;
635         case 0x020 ... 0x030:
636             s->window[(offset - 0x020) >> 2].wincon = val;
637             break;
638         case 0x034:
639             s->shadowcon = val;
640             break;
641         case 0x040 ... 0x088:
642             w = (offset - 0x040) >> 4;
643             i = ((offset - 0x040) & 0xF) >> 2;
644             if (i < 2) {
645                 s->window[w].vidosd[i] = val;
646             } else if (i == 3) {
647                 if (w != 1 && w != 2) {
648                     hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
649                              offset);
650                 } else {
651                     s->window[w].vidosd[i] = val;
652                 }
653             } else {
654                 if (w == 0) {
655                     i++;
656                 }
657                 s->window[w].vidosd[i] = val;
658             }
659             break;
660         case 0x0A0 ... 0x0C0:
661             w = (offset - 0x0A0) >> 3;
662             i = ((offset - 0x0A0) >> 2) & 1;
663             if (i == 1 && w >= 2) {
664                 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
665                          offset);
666             }
667             s->window[w].buf_start[i] = val;
668             break;
669         case 0x0D0 ... 0x0F0:
670             w = (offset - 0x0D0) >> 3;
671             i = ((offset - 0x0D0) >> 2) & 1;
672             if (i == 1 && w >= 2) {
673                 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
674                          offset);
675             }
676             s->window[w].buf_end[i] = val;
677             break;
678         case 0x100 ... 0x110:
679             s->window[(offset - 0x100) >> 2].buf_size = val;
680             break;
681         case 0x118 ... 0x11C:
682             s->vp1tcon[(offset - 0x118)] = val;
683             break;
684         case 0x130:
685             s->vidintcon[0] = val;
686         case 0x134:
687             s->vidintcon[1] &= ~(val & 7);
688             s5pc1xx_lcd_update_irq(s);
689             break;
690         case 0x140 ... 0x15C:
691             w = ((offset - 0x140) >> 3) + 1;
692             i = ((offset - 0x140) >> 2) & 1;
693             s->window[w].keycon[i] = val;
694             break;
695         case 0x170:
696             s->dithcon = val;
697             break;
698         case 0x180 ... 0x190:
699             s->window[(offset - 0x180) >> 2].winmap = val;
700             break;
701         case 0x19C ... 0x1A0:
702             s->wpalcon[(offset - 0x19C) >> 2] = val;
703             break;
704         case 0x1A4:
705             s->trigcon = val;
706             break;
707         case 0x1A8:
708             s->ituifcon = val;
709             break;
710         case 0x1B0 ... 0x1BC:
711             s->i80ifcon[(offset - 0x1B0) >> 2] = val;
712             break;
713         case 0x1D0 ... 0x1D4:
714             s->ldi_cmdcon[(offset - 0x1D0) >> 2] = val;
715             break;
716         case 0x1E0 ... 0x1E8:
717             i = (offset - 0x1E0) >> 2;
718             if (i == 2) {
719                 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
720                          offset);
721             }
722             s->sifccon[i] = val;
723             break;
724         case 0x200 ... 0x224:
725             w = ((offset - 0x200) >> 3);
726             i = ((offset - 0x200) >> 2) & 1;
727             s->window[w].vidw_alpha[i] = val;
728             break;
729         case 0x244 ... 0x250:
730             s->window[(offset - 0x244) >> 2].blendeq = val;
731             break;
732         case 0x260:
733             s->blendcon = val;
734             break;
735         case 0x27C:
736             s->dualrgb = val;
737             break;
738         case 0x280 ... 0x2AC:
739             s->ldi_cmd[(offset - 0x280) >> 2] = val;
740             break;
741         case 0x2400 ... 0x37FC: /* TODO: verify offset!!! */
742             w = (offset - 0x2400) >> 10;
743             i = ((offset - 0x2400) >> 2) & 0xFF;
744             s->window[w].palette[i] = val;
745             break;
746         default:
747             hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
748                      offset);
749     }
750 }
751
752 static uint32_t s5pc1xx_lcd_read(void *opaque, target_phys_addr_t offset)
753 {
754     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
755     int w, i;
756
757     if (offset & 3) {
758         hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n", offset);
759     }
760
761     switch (offset) {
762         case 0x000 ... 0x008:
763             return s->vidcon[(offset - 0x000) >> 2];
764         case 0x00C:
765             return s->prtcon;
766         case 0x010 ... 0x018:
767             return s->vidtcon[(offset - 0x010) >> 2];
768         case 0x020 ... 0x030:
769             return s->window[(offset - 0x020) >> 2].wincon;
770         case 0x034:
771             return s->shadowcon;
772         case 0x040 ... 0x088:
773             w = (offset - 0x040) >> 4;
774             i = ((offset - 0x040) & 0xF) >> 2;
775             if (i < 2) {
776                 return s->window[w].vidosd[i];
777             } else if (i == 3) {
778                 if (w != 1 && w != 2) {
779                     hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
780                              offset);
781                 } else {
782                     return s->window[w].vidosd[i];
783                 }
784             } else {
785                 if (w == 0) {
786                     i++;
787                 }
788                 return s->window[w].vidosd[i];
789             }
790         case 0x0A0 ... 0x0C0:
791             w = (offset - 0x0A0) >> 3;
792             i = ((offset - 0x0A0) >> 2) & 1;
793             if (i == 1 && w >= 2) {
794                 hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
795                          offset);
796             }
797             return s->window[w].buf_start[i];
798         case 0x0D0 ... 0x0F0:
799             w = (offset - 0x0D0) >> 3;
800             i = ((offset - 0x0D0) >> 2) & 1;
801             if (i == 1 && w >= 2) {
802                 hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
803                          offset);
804             }
805             return s->window[w].buf_end[i];
806         case 0x100 ... 0x110:
807             return s->window[(offset - 0x100) >> 2].buf_size;
808         case 0x118 ... 0x11C:
809             return s->vp1tcon[(offset - 0x118)];
810         case 0x130 ... 0x134:
811             return s->vidintcon[(offset - 0x130) >> 2];
812         case 0x140 ... 0x15C:
813             w = ((offset - 0x140) >> 3) + 1;
814             i = ((offset - 0x140) >> 2) & 1;
815             return s->window[w].keycon[i];
816         case 0x170:
817             return s->dithcon;
818         case 0x180 ... 0x190:
819             return s->window[(offset - 0x180) >> 2].winmap;
820         case 0x19C ... 0x1A0:
821             return s->wpalcon[(offset - 0x19C) >> 2];
822         case 0x1A4:
823             return s->trigcon;
824         case 0x1A8:
825             return s->ituifcon;
826         case 0x1B0 ... 0x1BC:
827             return s->i80ifcon[(offset - 0x1B0) >> 2];
828         case 0x1D0 ... 0x1D4:
829             return s->ldi_cmdcon[(offset - 0x1D0) >> 2];
830         case 0x1E0 ... 0x1E8:
831             i = (offset - 0x1E0) >> 2;
832             return s->sifccon[i];
833         case 0x200 ... 0x224:
834             w = ((offset - 0x200) >> 3);
835             i = ((offset - 0x200) >> 2) & 1;
836             return s->window[w].vidw_alpha[i];
837         case 0x244 ... 0x250:
838             return s->window[(offset - 0x244) >> 2].blendeq;
839         case 0x260:
840             return s->blendcon;
841         case 0x27C:
842             return s->dualrgb;
843         case 0x280 ... 0x2AC:
844             return s->ldi_cmd[(offset - 0x280) >> 2];
845         case 0x2400 ... 0x37FC: /* TODO: verify offset!!! */
846             w = (offset - 0x2400) >> 10;
847             i = ((offset - 0x2400) >> 2) & 0xFF;
848             return s->window[w].palette[i];
849         default:
850             hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
851                      offset);
852     }
853 }
854
855 static CPUReadMemoryFunc *s5pc1xx_lcd_readfn[] = {
856     s5pc1xx_lcd_read,
857     s5pc1xx_lcd_read,
858     s5pc1xx_lcd_read
859 };
860
861 static CPUWriteMemoryFunc *s5pc1xx_lcd_writefn[] = {
862     s5pc1xx_lcd_write,
863     s5pc1xx_lcd_write,
864     s5pc1xx_lcd_write
865 };
866
867 static void s5pc1xx_update_resolution(S5pc1xxLcdState *s)
868 {
869     uint32_t width, height;
870     /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */
871     width = (s->vidtcon[2] & 0x7FF) + 1;
872     height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
873     if (s->ifb == NULL || ds_get_width(s->console) != width ||
874         ds_get_height(s->console) != height) {
875
876         qemu_console_resize(s->console, width, height);
877         s->ifb = qemu_realloc(s->ifb, width * height * 7);
878         s->valid_line =
879             qemu_realloc(s->valid_line, (height >> 3) * sizeof(uint8_t));
880         s->valid_line_prev =
881             qemu_realloc(s->valid_line_prev, (height >> 3) * sizeof(uint8_t));
882         memset(s->ifb, 0, width * height * 7);
883         s->invalidate = 1;
884     }
885 }
886
887 /* Returns WxPAL for given window number WINDOW */
888 static uint32_t s5pc1xx_wxpal(S5pc1xxLcdState *s, int window)
889 {
890     switch (window) {
891     case 0:
892         return s->wpalcon[1] & 0x7;
893     case 1:
894         return (s->wpalcon[1] >> 3) & 0x7;
895     case 2:
896         return ((s->wpalcon[0] >> 8) & 0x6) | ((s->wpalcon[1] >> 6) & 0x1);
897     case 3:
898         return ((s->wpalcon[0] >> 12) & 0x6) | ((s->wpalcon[1] >> 7) & 0x1);
899     case 4:
900         return ((s->wpalcon[0] >> 16) & 0x6) | ((s->wpalcon[1] >> 8) & 0x1);
901     default:
902         hw_error("s5pc1xx.lcd: incorrect window number %d\n", window);
903     }
904 }
905
906 /* Parse BPPMODE_F bits and setup known DRAW_CONFIG fields accordingly.
907    BPPMODE_F = WINCON1[5:2] */
908 static void s5pc1xx_parse_win_bppmode(S5pc1xxLcdState *s,
909                                       DrawConfig *cfg, int window)
910 {
911     switch ((s->window[window].wincon >> 2) & 0xF) {
912     case 0:
913         cfg->draw_line = draw_line1;
914         cfg->is_palletized = 1;
915         cfg->bpp = 1;
916         break;
917     case 1:
918         cfg->draw_line = draw_line2;
919         cfg->is_palletized = 1;
920         cfg->bpp = 2;
921         break;
922     case 2:
923         cfg->draw_line = draw_line4;
924         cfg->is_palletized = 1;
925         cfg->bpp = 4;
926         break;
927     case 3:
928         cfg->draw_line = draw_line8;
929         cfg->is_palletized = 1;
930         cfg->bpp = 8;
931         break;
932     case 4:
933         cfg->draw_line = draw_line8;
934         cfg->is_palletized = 0;
935         cfg->pixel_to_rgb = pixel_a232_to_rgb;
936         cfg->bpp = 8;
937         break;
938     case 5:
939         cfg->draw_line = draw_line16;
940         cfg->is_palletized = 0;
941         cfg->pixel_to_rgb = pixel_565_to_rgb;
942         cfg->bpp = 16;
943         break;
944     case 6:
945         cfg->draw_line = draw_line16;
946         cfg->is_palletized = 0;
947         cfg->pixel_to_rgb = pixel_a555_to_rgb;
948         cfg->bpp = 16;
949         break;
950     case 7:
951         cfg->draw_line = draw_line16;
952         cfg->is_palletized = 0;
953         cfg->pixel_to_rgb = pixel_1555_to_rgb;
954         cfg->bpp = 16;
955         break;
956     case 8:
957         cfg->draw_line = draw_line32;
958         cfg->is_palletized = 0;
959         cfg->pixel_to_rgb = pixel_666_to_rgb;
960         cfg->bpp = 32;
961         break;
962     case 9:
963         cfg->draw_line = draw_line32;
964         cfg->is_palletized = 0;
965         cfg->pixel_to_rgb = pixel_a665_to_rgb;
966         cfg->bpp = 32;
967         break;
968     case 10:
969         cfg->draw_line = draw_line32;
970         cfg->is_palletized = 0;
971         cfg->pixel_to_rgb = pixel_a666_to_rgb;
972         cfg->bpp = 32;
973         break;
974     case 11:
975         cfg->draw_line = draw_line32;
976         cfg->is_palletized = 0;
977         cfg->pixel_to_rgb = pixel_888_to_rgb;
978         cfg->bpp = 32;
979         break;
980     case 12:
981         cfg->draw_line = draw_line32;
982         cfg->is_palletized = 0;
983         cfg->pixel_to_rgb = pixel_a887_to_rgb;
984         cfg->bpp = 32;
985         break;
986     case 13:
987         cfg->draw_line = draw_line32;
988         cfg->is_palletized = 0;
989         if ((s->window[window].wincon & (1 << 6)) &&
990             (s->window[window].wincon & 2)) {
991             cfg->pixel_to_rgb = pixel_8888_to_rgb;
992             cfg->fg_alpha_pix = 1;
993         } else {
994             cfg->pixel_to_rgb = pixel_a888_to_rgb;
995         }
996         cfg->bpp = 32;
997         break;
998     case 14:
999         cfg->draw_line = draw_line16;
1000         cfg->is_palletized = 0;
1001         if ((s->window[window].wincon & (1 << 6)) &&
1002             (s->window[window].wincon & 2)) {
1003             cfg->pixel_to_rgb = pixel_4444_to_rgb;
1004             cfg->fg_alpha_pix = 1;
1005         } else {
1006             cfg->pixel_to_rgb = pixel_a444_to_rgb;
1007         }
1008         cfg->bpp = 16;
1009         break;
1010     case 15:
1011         cfg->draw_line = draw_line16;
1012         cfg->is_palletized = 0;
1013         cfg->pixel_to_rgb = pixel_555_to_rgb;
1014         cfg->bpp = 16;
1015         break;
1016     }
1017 }
1018
1019 pixel_to_rgb_func *wxpal_to_rgb[8] = {
1020     [0] = pixel_565_to_rgb,
1021     [1] = pixel_a555_to_rgb,
1022     [2] = pixel_666_to_rgb,
1023     [3] = pixel_a665_to_rgb,
1024     [4] = pixel_a666_to_rgb,
1025     [5] = pixel_888_to_rgb,
1026     [6] = pixel_a888_to_rgb,
1027     [7] = pixel_8888_to_rgb
1028 };
1029
1030 static inline uint32_t unpack_by_4(uint32_t x)
1031 {
1032     return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
1033 }
1034
1035 static coef_func *coef_decode(uint32_t x)
1036 {
1037     switch (x) {
1038     case 0:
1039         return coef_zero;
1040     case 1:
1041         return coef_one;
1042     case 2:
1043         return coef_alphaa;
1044     case 3:
1045         return coef_one_minus_alphaa;
1046     case 4:
1047         return coef_alphab;
1048     case 5:
1049         return coef_one_minus_alphab;
1050     case 10:
1051         return coef_a;
1052     case 11:
1053         return coef_one_minus_a;
1054     case 12:
1055         return coef_b;
1056     case 13:
1057         return coef_one_minus_b;
1058     default:
1059         hw_error("s5pc1xx.lcd: illegal value\n");
1060     }
1061 }
1062
1063 static inline void putpixel_by_bpp(DrawConfig *cfg, int bpp)
1064 {
1065     switch (bpp) {
1066     case 8:
1067         cfg->put_pixel = put_pixel8;
1068         break;
1069     case 15:
1070         cfg->put_pixel = put_pixel15;
1071         break;
1072     case 16:
1073         cfg->put_pixel = put_pixel16;
1074         break;
1075     case 24:
1076         cfg->put_pixel = put_pixel24;
1077         break;
1078     case 32:
1079         cfg->put_pixel = put_pixel32;
1080         break;
1081     default:
1082         hw_error("s5pc1xx.lcd: unsupported BPP (%d)", bpp);
1083     }
1084 }
1085
1086 static void s5pc1xx_lcd_update(void *opaque)
1087 {
1088     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1089     DrawConfig cfg;
1090     int i, dirty[2], x;
1091     int line;
1092     target_phys_addr_t scanline, newline, map_len, pd, inc_size;
1093     uint8_t *mapline, *startline, *valid_line_tmp;
1094     int lefttop_x, lefttop_y, rightbottom_x, rightbottom_y;
1095     int ext_line_size;
1096     int width, height;
1097     uint32_t tmp;
1098     int buf_id;
1099     int need_redraw;
1100     int global_width, global_height;
1101     int bpp;
1102     uint8_t *d;
1103     uint8_t is_first_window;
1104
1105     if (!s || !s->console || !ds_get_bits_per_pixel(s->console)) {
1106         return;
1107     }
1108
1109     if (! (s->vidcon[0] & 2)) {
1110         return;
1111     }
1112
1113     memset(&cfg, 0, sizeof(cfg));
1114
1115     s5pc1xx_update_resolution(s);
1116
1117     /* First we will mark lines of the display which need to be redrawn */
1118     memset(s->valid_line, 0xFF,
1119            ((((s->vidtcon[2] >> 11) & 0x7FF) + 1 + 7) >> 3) * sizeof(uint8_t));
1120     for (i = 0; i < 5; i++) {
1121         if (s->window[i].wincon & 1) {
1122             lefttop_x = (s->window[i].vidosd[0] >> 11) & 0x7FF;
1123             lefttop_y = (s->window[i].vidosd[0] >>  0) & 0x7FF;
1124             rightbottom_x = (s->window[i].vidosd[1] >> 11) & 0x7FF;
1125             rightbottom_y = (s->window[i].vidosd[1] >>  0) & 0x7FF;
1126             height = rightbottom_y - lefttop_y + 1;
1127             width = rightbottom_x - lefttop_x + 1;
1128             ext_line_size = s->window[i].buf_size & 0x1FFF;
1129             buf_id = 0;
1130             if (i <= 1) {
1131                 buf_id = (s->window[i].wincon >> 20) & 1;
1132             }
1133             /* According to documentation framebuffer is always located in
1134                single bank of DRAM. Bits [31:24] of BUF_START encode bank
1135                number, and [23:0] - address of the buffer in bank. We will
1136                assume that DRAM Controller uses linear memory mapping so
1137                BUF_START will be just address of the framebuffer. In the
1138                other case framebuffer will be dispersed all over the system
1139                memory so it is unclear how such system will work.
1140
1141                Moreover, we will ignore absence of carry bit bitween bits 23
1142                and 24 while incrementing address in the hope that no
1143                programmer will use such hack. */
1144             scanline = s->window[i].buf_start[buf_id];
1145             inc_size = (s->window[i].buf_size & 0x1FFF) +
1146                        ((s->window[i].buf_size >> 13) & 0x1FFF);
1147             cpu_physical_sync_dirty_bitmap(scanline,
1148                                            scanline + height * inc_size);
1149             pd = (cpu_get_physical_page_desc(scanline) & TARGET_PAGE_MASK) +
1150                  (scanline & ~TARGET_PAGE_MASK);
1151             dirty[0] = dirty[1] =
1152                 cpu_physical_memory_get_dirty(scanline, VGA_DIRTY_FLAG);
1153             cpu_physical_memory_reset_dirty(scanline, scanline, VGA_DIRTY_FLAG);
1154             for (line = 0; line < height; line++) {
1155                 newline = scanline + ext_line_size;
1156                 for (x = scanline;
1157                      x < newline;
1158                      x += TARGET_PAGE_SIZE) {
1159                     pd = (cpu_get_physical_page_desc(x) & TARGET_PAGE_MASK) +
1160                          (x & ~TARGET_PAGE_MASK);
1161                     dirty[1] = cpu_physical_memory_get_dirty(pd, VGA_DIRTY_FLAG);
1162                     dirty[0] |= dirty[1];
1163                 }
1164                 if (dirty[0]) {
1165                     tmp = line + lefttop_y;
1166                     s->valid_line[tmp >> 3] &= ~(1 << (tmp & 0x7));
1167                 }
1168                 dirty[0] = dirty[1] = 0;
1169                 scanline += (s->window[i].buf_size & 0x1FFF) +
1170                     ((s->window[i].buf_size >> 13) & 0x1FFF);
1171             }
1172             scanline = s->window[i].buf_start[buf_id];
1173             pd = (cpu_get_physical_page_desc(scanline) & TARGET_PAGE_MASK) +
1174                  (scanline & ~TARGET_PAGE_MASK);
1175             cpu_physical_memory_reset_dirty(pd, pd + inc_size * height,
1176                                             VGA_DIRTY_FLAG);
1177         }
1178     }
1179
1180     need_redraw = 0;
1181     is_first_window = 1;
1182     for (i = 0; i < 5; i++) {
1183         if (s->window[i].wincon & 1) {
1184             cfg.fg_alpha_pix = 0;
1185             s5pc1xx_parse_win_bppmode(s, &cfg, i);
1186             /* If we have mode with palletized color then we need to parse
1187                palette color mode and set pixel-to-rgb conversion function
1188                accordingly. */
1189             if (cfg.is_palletized) {
1190                 tmp = s5pc1xx_wxpal(s, i);
1191                 /* Different windows have different mapping WxPAL to palette
1192                    pixel format. This transform happens to unify them all. */
1193                 if (i < 2 && tmp < 7) {
1194                     tmp = 6 - tmp;
1195                 }
1196                 cfg.pixel_to_rgb = wxpal_to_rgb[tmp];
1197                 if (tmp == 7) {
1198                     cfg.fg_alpha_pix = 1;
1199                 }
1200             }
1201             cfg.put_pixel = put_rgba;
1202             cfg.get_pixel = get_rgba;
1203             cfg.bg_alpha_pix = 1;
1204             cfg.color_mask = s->window[i].keycon[0] & 0xFFFFFF;
1205             cfg.color_key = s->window[i].keycon[1];
1206             cfg.color_ctl = (s->window[i].keycon[0] >> 24) & 7;
1207             if (i == 0) {
1208                 cfg.fg_alpha[0] = s->window[i].vidw_alpha[0];
1209                 cfg.fg_alpha[1] = s->window[i].vidw_alpha[1];
1210             } else {
1211                 cfg.fg_alpha[0] =
1212                     unpack_by_4((s->window[i].vidosd[3] & 0xFFF000) >> 12) |
1213                     (s->window[i].vidw_alpha[0] & 0xF0F0F);
1214                 cfg.fg_alpha[1] =
1215                     unpack_by_4(s->window[i].vidosd[3] & 0xFFF) |
1216                     (s->window[i].vidw_alpha[0] & 0xF0F0F);
1217             }
1218             cfg.bg_pixel_blending = 1;
1219             cfg.fg_pixel_blending = s->window[i].wincon & (1 << 6);
1220             cfg.fg_alpha_sel = (s->window[i].wincon >> 1) & 1;
1221             cfg.palette = s->window[i].palette;
1222             cfg.swap = (s->window[i].wincon >> 15) & 0xF;
1223             cfg.coef_q = coef_decode((s->window[i].blendeq >> 18) & 0xF);
1224             cfg.coef_p = coef_decode((s->window[i].blendeq >> 12) & 0xF);
1225             cfg.coef_b = coef_decode((s->window[i].blendeq >>  6) & 0xF);
1226             cfg.coef_a = coef_decode((s->window[i].blendeq >>  0) & 0xF);
1227             if (is_first_window) {
1228                 cfg.blend = NULL;
1229             } else {
1230                 cfg.blend = blend_colorkey;
1231             }
1232             is_first_window = 0;
1233             /* At this point CFG is fully set up except WIDTH. We can proceed
1234                with drawing. */
1235             lefttop_x = (s->window[i].vidosd[0] >> 11) & 0x7FF;
1236             lefttop_y = (s->window[i].vidosd[0] >>  0) & 0x7FF;
1237             rightbottom_x = (s->window[i].vidosd[1] >> 11) & 0x7FF;
1238             rightbottom_y = (s->window[i].vidosd[1] >>  0) & 0x7FF;
1239             height = rightbottom_y - lefttop_y + 1;
1240             width = rightbottom_x - lefttop_x + 1;
1241             cfg.width = width;
1242             ext_line_size = (width * cfg.bpp) >> 3;
1243             buf_id = 0;
1244             if (i <= 1) {
1245                 buf_id = (s->window[i].wincon >> 20) & 1;
1246             }
1247             scanline = s->window[i].buf_start[buf_id];
1248             global_width = (s->vidtcon[2] & 0x7FF) + 1;
1249             global_height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
1250             /* See comment above about DRAM Controller memory mapping. */
1251             map_len = ((s->window[i].buf_size & 0x1FFF) +
1252                     ((s->window[i].buf_size >> 13) & 0x1FFF)) * height;
1253             mapline = cpu_physical_memory_map(scanline, &map_len, 0);
1254             if (!mapline) {
1255                 return;
1256             }
1257             startline = mapline;
1258             for (line = 0; line < height; line++) {
1259                 tmp = line + lefttop_y;
1260                 if (s->invalidate ||
1261                     !(s->valid_line[tmp >> 3] & (1 << (tmp & 7))) ||
1262                     !(s->valid_line_prev[tmp >> 3] & (1 << (tmp & 7)))) {
1263                     need_redraw = 1;
1264                     cfg.draw_line(&cfg, mapline,
1265                                   s->ifb + lefttop_x * 7 +
1266                                       (lefttop_y + line) * global_width * 7,
1267                                   s->ifb + lefttop_x * 7 +
1268                                       (lefttop_y + line) * global_width * 7);
1269                 }
1270                 mapline += (s->window[i].buf_size & 0x1FFF) +
1271                     ((s->window[i].buf_size >> 13) & 0x1FFF);
1272             }
1273             cpu_physical_memory_unmap(startline, map_len, 0, 0);
1274         }
1275     }
1276     /* Last pass: copy resulting image to QEMU_CONSOLE. */
1277     if (need_redraw) {
1278         width = (s->vidtcon[2] & 0x7FF) + 1;
1279         height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
1280         cfg.get_pixel = get_rgba;
1281         bpp = ds_get_bits_per_pixel(s->console);
1282         putpixel_by_bpp(&cfg, bpp);
1283         bpp = (bpp + 1) >> 3;
1284         d = ds_get_data(s->console);
1285         for (line = 0; line < height; line++) {
1286             draw_line_copy(&cfg, s->ifb + width * line * 7,
1287                            d + width * line * bpp, NULL);
1288         }
1289         dpy_update(s->console, 0, 0, width, height);
1290     }
1291     valid_line_tmp = s->valid_line;
1292     s->valid_line = s->valid_line_prev;
1293     s->valid_line_prev = valid_line_tmp;
1294     s->invalidate = 0;
1295     s->vidintcon[1] |= 2;
1296     s5pc1xx_lcd_update_irq(s);
1297 }
1298
1299 static void s5pc1xx_lcd_save(QEMUFile *f, void *opaque)
1300 {
1301     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1302     int i, j, width, height;
1303
1304     width = (s->vidtcon[2] & 0x7FF) + 1;
1305     height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
1306
1307     qemu_put_be32s(f, &s->shadowcon);
1308     qemu_put_be32s(f, &s->prtcon);
1309     qemu_put_be32s(f, &s->dithcon);
1310     qemu_put_be32s(f, &s->trigcon);
1311     qemu_put_be32s(f, &s->ituifcon);
1312     qemu_put_be32s(f, &s->blendcon);
1313     qemu_put_be32s(f, &s->dualrgb);
1314
1315     for (i = 0; i < 2; i++) {
1316         qemu_put_be32s(f, &s->vp1tcon[i]);
1317         qemu_put_be32s(f, &s->vidintcon[i]);
1318         qemu_put_be32s(f, &s->wpalcon[i]);
1319         qemu_put_be32s(f, &s->ldi_cmdcon[i]);
1320     }
1321
1322     for (i = 0; i < 3; i++) {
1323         qemu_put_be32s(f, &s->vidcon[i]);
1324         qemu_put_be32s(f, &s->vidtcon[i]);
1325         qemu_put_be32s(f, &s->sifccon[i]);
1326     }
1327
1328     for (i = 0; i < 4; i++) {
1329         qemu_put_be32s(f, &s->i80ifcon[i]);
1330     }
1331
1332     for (i = 0; i < 12; i++) {
1333         qemu_put_be32s(f, &s->ldi_cmd[i]);
1334     }
1335
1336     //qemu_put_buffer(f, s->ifb, width * height * 7);
1337
1338     /* Not saving valid_line because the whole screen will be redrawn anyway */
1339
1340     for (i = 0; i < 5; i++) {
1341         qemu_put_be32s(f, &s->window[i].wincon);
1342         qemu_put_be32s(f, &s->window[i].buf_size);
1343         qemu_put_be32s(f, &s->window[i].winmap);
1344         qemu_put_be32s(f, &s->window[i].blendeq);
1345
1346         for (j = 0; j < 2; j++) {
1347             qemu_put_be32s(f, &s->window[i].buf_start[j]);
1348             qemu_put_be32s(f, &s->window[i].buf_end[j]);
1349             qemu_put_be32s(f, &s->window[i].keycon[j]);
1350             qemu_put_be32s(f, &s->window[i].vidw_alpha[j]);
1351         }
1352
1353         for (j = 0; j < 4; j++) {
1354             qemu_put_be32s(f, &s->window[i].vidosd[j]);
1355         }
1356
1357         for (j = 0; j < 256; j++) {
1358             qemu_put_be32s(f, &s->window[i].palette[j]);
1359         }
1360     }
1361 }
1362
1363 static int s5pc1xx_lcd_load(QEMUFile *f, void *opaque, int version_id)
1364 {
1365     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1366     int i, j, width, height;
1367
1368     if (version_id != 1) {
1369         return -EINVAL;
1370     }
1371
1372     qemu_get_be32s(f, &s->shadowcon);
1373     qemu_get_be32s(f, &s->prtcon);
1374     qemu_get_be32s(f, &s->dithcon);
1375     qemu_get_be32s(f, &s->trigcon);
1376     qemu_get_be32s(f, &s->ituifcon);
1377     qemu_get_be32s(f, &s->blendcon);
1378     qemu_get_be32s(f, &s->dualrgb);
1379
1380     for (i = 0; i < 2; i++) {
1381         qemu_get_be32s(f, &s->vp1tcon[i]);
1382         qemu_get_be32s(f, &s->vidintcon[i]);
1383         qemu_get_be32s(f, &s->wpalcon[i]);
1384         qemu_get_be32s(f, &s->ldi_cmdcon[i]);
1385     }
1386
1387     for (i = 0; i < 3; i++) {
1388         qemu_get_be32s(f, &s->vidcon[i]);
1389         qemu_get_be32s(f, &s->vidtcon[i]);
1390         qemu_get_be32s(f, &s->sifccon[i]);
1391     }
1392
1393     width = (s->vidtcon[2] & 0x7FF) + 1;
1394     height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
1395
1396     for (i = 0; i < 4; i++) {
1397         qemu_get_be32s(f, &s->i80ifcon[i]);
1398     }
1399
1400     for (i = 0; i < 12; i++) {
1401         qemu_get_be32s(f, &s->ldi_cmd[i]);
1402     }
1403
1404     //qemu_get_buffer(f, s->ifb, width * height * 7);
1405
1406     for (i = 0; i < 5; i++) {
1407         qemu_get_be32s(f, &s->window[i].wincon);
1408         qemu_get_be32s(f, &s->window[i].buf_size);
1409         qemu_get_be32s(f, &s->window[i].winmap);
1410         qemu_get_be32s(f, &s->window[i].blendeq);
1411
1412         for (j = 0; j < 2; j++) {
1413             qemu_get_be32s(f, &s->window[i].buf_start[j]);
1414             qemu_get_be32s(f, &s->window[i].buf_end[j]);
1415             qemu_get_be32s(f, &s->window[i].keycon[j]);
1416             qemu_get_be32s(f, &s->window[i].vidw_alpha[j]);
1417         }
1418
1419         for (j = 0; j < 4; j++) {
1420             qemu_get_be32s(f, &s->window[i].vidosd[j]);
1421         }
1422
1423         for (j = 0; j < 256; j++) {
1424             qemu_get_be32s(f, &s->window[i].palette[j]);
1425         }
1426     }
1427
1428     s5pc1xx_update_resolution(s);
1429     /* Redraw the whole screen */
1430     s->invalidate = 1;
1431     return 0;
1432 }
1433
1434 static void s5pc1xx_lcd_invalidate(void *opaque)
1435 {
1436     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1437     s->invalidate = 1;
1438 }
1439
1440 static void s5pc1xx_window_reset(S5pc1xxLcdWindow *s)
1441 {
1442     memset(s, 0, sizeof(*s));
1443     s->blendeq = 0xC2;
1444 }
1445
1446 static void s5pc1xx_lcd_reset(void *opaque)
1447 {
1448     S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1449     int i;
1450
1451     memset((uint8_t *)s + sizeof(SysBusDevice),
1452             0, offsetof(S5pc1xxLcdState, window));
1453     for (i = 0; i < 5; i++) {
1454         s5pc1xx_window_reset(&s->window[i]);
1455     }
1456     if (s->ifb != NULL) {
1457         qemu_free(s->ifb);
1458     }
1459     s->ifb = NULL;
1460     if (s->valid_line != NULL) {
1461         qemu_free(s->valid_line);
1462     }
1463     s->valid_line = NULL;
1464     if (s->valid_line_prev != NULL) {
1465         qemu_free(s->valid_line_prev);
1466     }
1467     s->valid_line_prev = NULL;
1468 }
1469
1470 static int s5pc1xx_lcd_init(SysBusDevice *dev)
1471 {
1472     int iomemtype;
1473     S5pc1xxLcdState *s = FROM_SYSBUS(S5pc1xxLcdState, dev);
1474
1475     s->ifb = NULL;
1476     s->valid_line = NULL;
1477     s->valid_line_prev = NULL;
1478     s5pc1xx_lcd_reset(s);
1479
1480     sysbus_init_irq(dev, &s->irq[0]);
1481     sysbus_init_irq(dev, &s->irq[1]);
1482     sysbus_init_irq(dev, &s->irq[2]);
1483
1484     iomemtype =
1485         cpu_register_io_memory(s5pc1xx_lcd_readfn, s5pc1xx_lcd_writefn, s,
1486                                                         DEVICE_NATIVE_ENDIAN);
1487     sysbus_init_mmio(dev, 0x3800, iomemtype);
1488
1489     s->console = graphic_console_init(s5pc1xx_lcd_update,
1490                                       s5pc1xx_lcd_invalidate, NULL, NULL, s);
1491
1492     qemu_register_reset(s5pc1xx_lcd_reset, s);
1493     register_savevm(&dev->qdev, "s5pc1xx.lcd", -1, 1,
1494                     s5pc1xx_lcd_save, s5pc1xx_lcd_load, s);
1495
1496     return 0;
1497 }
1498
1499 static void s5pc1xx_lcd_register_devices(void)
1500 {
1501     sysbus_register_dev("s5pc1xx.lcd", sizeof(S5pc1xxLcdState),
1502                         s5pc1xx_lcd_init);
1503 }
1504
1505 device_init(s5pc1xx_lcd_register_devices)