2 * S5PC1XX LCD Controller
4 * Copyright (c) 2010 Samsung Electronics.
5 * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
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
18 #include "pixel_ops.h"
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);
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;
56 uint8_t fg_pixel_blending, bg_pixel_blending;
57 uint8_t fg_alpha_sel, bg_alpha_sel;
60 typedef struct S5pc1xxLcdWindow {
63 uint32_t buf_start[2];
68 uint32_t vidw_alpha[2];
70 uint32_t palette[256];
81 uint32_t vidintcon[2];
87 uint32_t ldi_cmdcon[2];
93 S5pc1xxLcdWindow window[5];
96 uint8_t *valid_line_prev;
97 DisplayState *console;
103 /* Palette/pixel to RGB conversion */
105 #define DEF_PIXEL_TO_RGB(N,R,G,B,A) \
106 static void N(uint32_t pixel, rgba *p) \
108 p->b = (pixel & ((1 << (B)) - 1)) << (8 - (B)); \
110 p->g = (pixel & ((1 << (G)) - 1)) << (8 - (G)); \
112 p->r = (pixel & ((1 << (R)) - 1)) << (8 - (R)); \
116 } else if (8 == (A)) { \
117 p->a = pixel & 0xFF; \
118 p->a = (p->a << 16) | (p->a << 8) | p->a; \
120 p->a = (pixel & ((1 << (A)) - 1)) << (8 - (A)); \
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)
138 /* Special case for (5+1,5+1,5+1) mode */
139 static void pixel_1555_to_rgb(uint32_t pixel, rgba *p)
141 uint8_t u = (pixel >> 15) & 1;
142 p->b = (((pixel & 0x1F) << 1) | u) << 2;
144 p->g = (((pixel & 0x3F) << 1) | u) << 2;
146 p->r = (((pixel & 0x1F) << 1) | u) << 2;
150 /* Write RGB to QEMU's GraphicConsole framebuffer */
152 static int put_pixel8(rgba p, uint8_t *d)
154 uint32_t pixel = rgb_to_pixel8(p.r, p.g, p.b);
155 *(uint8_t *)d = pixel;
159 static int put_pixel15(rgba p, uint8_t *d)
161 uint32_t pixel = rgb_to_pixel15(p.r, p.g, p.b);
162 *(uint16_t *)d = pixel;
166 static int put_pixel16(rgba p, uint8_t *d)
168 uint32_t pixel = rgb_to_pixel16(p.r, p.g, p.b);
169 *(uint16_t *)d = pixel;
173 static int put_pixel24(rgba p, uint8_t *d)
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;
182 static int put_pixel32(rgba p, uint8_t *d)
184 uint32_t pixel = rgb_to_pixel24(p.r, p.g, p.b);
185 *(uint32_t *)d = pixel;
190 /* Put/get pixel to/from internal LCD Controller framebuffer */
192 static int put_rgba(rgba p, uint8_t *d)
194 *(uint8_t *)d++ = p.r;
195 *(uint8_t *)d++ = p.g;
196 *(uint8_t *)d++ = p.b;
197 *(uint32_t *)d = p.a;
201 static int get_rgba(uint8_t *s, rgba *p)
203 p->r = *(uint8_t *)s++;
204 p->g = *(uint8_t *)s++;
205 p->b = *(uint8_t *)s++;
206 p->a = *(uint32_t *)s;
211 /* Perform byte/halfword/word swap of data accrding to config */
213 static inline uint64_t swap_data(const DrawConfig *cfg, uint64_t x)
220 if (cfg->swap & BISWP) {
222 for (i = 0; i < 64; i++) {
223 if (x & (1ULL << (64 - i))) {
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);
239 if (cfg->swap & HWSWP) {
240 x = ((x & 0x000000000000FFFFULL) << 48) |
241 ((x & 0x00000000FFFF0000ULL) << 16) |
242 ((x & 0x0000FFFF00000000ULL) >> 16) |
243 ((x & 0xFFFF000000000000ULL) >> 48);
245 if (cfg->swap & WSWP) {
246 x = ((x & 0x00000000FFFFFFFFULL) << 32) |
247 ((x & 0xFFFFFFFF00000000ULL) >> 32);
253 /* Coefficient extraction functions */
255 static uint32_t coef_zero(const DrawConfig *cfg,
261 static uint32_t coef_one(const DrawConfig *cfg,
267 static uint32_t coef_alphaa(const DrawConfig *cfg,
270 if (!cfg->fg_pixel_blending) {
271 pa.a = cfg->fg_alpha_sel;
273 if (cfg->fg_alpha_pix) {
276 return cfg->fg_alpha[pa.a];
280 static uint32_t coef_one_minus_alphaa(const DrawConfig *cfg,
283 if (!cfg->fg_pixel_blending) {
284 pa.a = cfg->fg_alpha_sel;
286 if (cfg->fg_alpha_pix) {
287 return 0xFFFFFF - pa.a;
289 return 0xFFFFFF - cfg->fg_alpha[pa.a];
293 static uint32_t coef_alphab(const DrawConfig *cfg,
296 if (!cfg->bg_pixel_blending) {
297 pb.a = cfg->bg_alpha_sel;
299 if (cfg->bg_alpha_pix) {
302 return cfg->bg_alpha[pb.a];
306 static uint32_t coef_one_minus_alphab(const DrawConfig *cfg,
309 if (!cfg->bg_pixel_blending) {
310 pb.a = cfg->bg_alpha_sel;
312 if (cfg->bg_alpha_pix) {
313 return 0xFFFFFF - pb.a;
315 return 0xFFFFFF - cfg->bg_alpha[pb.a];
319 static uint32_t coef_a(const DrawConfig *cfg,
322 return (pa.r << 16) | (pa.g << 8) | pa.b;
325 static uint32_t coef_one_minus_a(const DrawConfig *cfg,
328 return 0xFFFFFF - ((pa.r << 16) | (pa.g << 8) | pa.b);
331 static uint32_t coef_b(const DrawConfig *cfg,
334 return (pb.r << 16) | (pb.g << 8) | pb.b;
337 static uint32_t coef_one_minus_b(const DrawConfig *cfg,
340 return 0xFFFFFF - ((pb.r << 16) | (pb.g << 8) | pb.b);
344 /* Blending functions */
346 static void blend_alpha(const DrawConfig *cfg,
347 rgba p_bg, rgba p_fg, rgba *res)
349 uint32_t pl, ql, al, bl;
350 uint32_t p, q, a, b, fg, bg, fga, bga;
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);
368 if (cfg->fg_pixel_blending) {
369 if (cfg->fg_alpha_pix) {
372 fga = cfg->fg_alpha[p_fg.a] & 0xFF;
375 fga = cfg->fg_alpha[cfg->fg_alpha_sel] & 0xFF;
377 if (cfg->bg_pixel_blending) {
378 if (cfg->bg_alpha_pix) {
381 bga = cfg->bg_alpha[p_bg.a] & 0xFF;
384 bga = cfg->bg_alpha[cfg->bg_alpha_sel] & 0xFF;
386 bg = (bg * b + fg * a) / 0xFF;
392 bga = (bga * p + fga * q) / 0xFF;
409 if (cfg->fg_pixel_blending) {
410 if (cfg->fg_alpha_pix) {
411 fga = (p_fg.a >> 8) & 0xFF;
413 fga = (cfg->fg_alpha[p_fg.a] >> 8) & 0xFF;
416 fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 8) & 0xFF;
418 if (cfg->bg_pixel_blending) {
419 if (cfg->bg_alpha_pix) {
420 bga = (p_bg.a >> 8) & 0xFF;
422 bga = (cfg->bg_alpha[p_bg.a] >> 8) & 0xFF;
425 bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 8) & 0xFF;
427 bg = (bg * b + fg * a) / 0xFF;
433 bga = (bga * p + fga * q) / 0xFF;
450 if (cfg->fg_pixel_blending) {
451 if (cfg->fg_alpha_pix) {
452 fga = (p_fg.a >> 16) & 0xFF;
454 fga = (cfg->fg_alpha[p_fg.a] >> 16) & 0xFF;
457 fga = (cfg->fg_alpha[cfg->fg_alpha_sel] >> 16) & 0xFF;
459 if (cfg->bg_pixel_blending) {
460 if (cfg->bg_alpha_pix) {
461 bga = (p_bg.a >> 16) & 0xFF;
463 bga = (cfg->bg_alpha[p_bg.a] >> 16) & 0xFF;
466 bga = (cfg->bg_alpha[cfg->bg_alpha_sel] >> 16) & 0xFF;
468 bg = (bg * b + fg * a) / 0xFF;
474 bga = (bga * p + fga * q) / 0xFF;
476 res->a |= 0xFF << 16;
482 static void blend_colorkey(DrawConfig *cfg,
483 rgba p_bg, rgba p_fg, rgba *p)
487 if (cfg->color_ctl & 2) {
488 blend_alpha(cfg, p_bg, p_fg, p);
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) {
500 cfg->fg_pixel_blending = 0;
501 blend_alpha(cfg, p_bg, p_fg, p);
506 if (cfg->color_ctl & 4) {
508 cfg->fg_pixel_blending = 0;
509 blend_alpha(cfg, p_bg, p_fg, p);
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) {
520 cfg->fg_pixel_blending = 0;
521 blend_alpha(cfg, p_bg, p_fg, p);
526 if (cfg->color_ctl & 4) {
528 cfg->fg_pixel_blending = 0;
529 blend_alpha(cfg, p_bg, p_fg, p);
538 /* Draw line functions */
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) \
546 int width = cfg->width; \
549 data = ldq_raw((void *)src); \
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); \
557 ifb += cfg->get_pixel(ifb, &p_old); \
558 cfg->blend(cfg, p_old, p, &p); \
560 dst += cfg->put_pixel(p, dst); \
563 width -= (64 / (N)); \
564 } while (width > 0); \
574 static void draw_line_copy(DrawConfig *cfg, uint8_t *src, uint8_t *dst,
578 int width = cfg->width;
581 src += cfg->get_pixel(src, &p);
582 dst += cfg->put_pixel(p, dst);
590 static void s5pc1xx_lcd_update_irq(S5pc1xxLcdState *s)
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]);
598 if ((s->vidintcon[0] & 2) && (s->vidintcon[1] & 1)) {
599 qemu_irq_raise(s->irq[0]);
601 qemu_irq_lower(s->irq[0]);
603 if ((s->vidintcon[0] & (1 << 12)) && (s->vidintcon[1] & 2)) {
604 qemu_irq_raise(s->irq[1]);
606 qemu_irq_lower(s->irq[1]);
608 if ((s->vidintcon[0] & (1 << 17)) && (s->vidintcon[1] & 4)) {
609 qemu_irq_raise(s->irq[2]);
611 qemu_irq_lower(s->irq[2]);
615 static void s5pc1xx_lcd_write(void *opaque, target_phys_addr_t offset,
618 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
622 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n", offset);
626 case 0x000 ... 0x008:
627 s->vidcon[(offset - 0x000) >> 2] = val;
632 case 0x010 ... 0x018:
633 s->vidtcon[(offset - 0x010) >> 2] = val;
635 case 0x020 ... 0x030:
636 s->window[(offset - 0x020) >> 2].wincon = val;
641 case 0x040 ... 0x088:
642 w = (offset - 0x040) >> 4;
643 i = ((offset - 0x040) & 0xF) >> 2;
645 s->window[w].vidosd[i] = val;
647 if (w != 1 && w != 2) {
648 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
651 s->window[w].vidosd[i] = val;
657 s->window[w].vidosd[i] = val;
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",
667 s->window[w].buf_start[i] = val;
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",
676 s->window[w].buf_end[i] = val;
678 case 0x100 ... 0x110:
679 s->window[(offset - 0x100) >> 2].buf_size = val;
681 case 0x118 ... 0x11C:
682 s->vp1tcon[(offset - 0x118)] = val;
685 s->vidintcon[0] = val;
687 s->vidintcon[1] &= ~(val & 7);
688 s5pc1xx_lcd_update_irq(s);
690 case 0x140 ... 0x15C:
691 w = ((offset - 0x140) >> 3) + 1;
692 i = ((offset - 0x140) >> 2) & 1;
693 s->window[w].keycon[i] = val;
698 case 0x180 ... 0x190:
699 s->window[(offset - 0x180) >> 2].winmap = val;
701 case 0x19C ... 0x1A0:
702 s->wpalcon[(offset - 0x19C) >> 2] = val;
710 case 0x1B0 ... 0x1BC:
711 s->i80ifcon[(offset - 0x1B0) >> 2] = val;
713 case 0x1D0 ... 0x1D4:
714 s->ldi_cmdcon[(offset - 0x1D0) >> 2] = val;
716 case 0x1E0 ... 0x1E8:
717 i = (offset - 0x1E0) >> 2;
719 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
724 case 0x200 ... 0x224:
725 w = ((offset - 0x200) >> 3);
726 i = ((offset - 0x200) >> 2) & 1;
727 s->window[w].vidw_alpha[i] = val;
729 case 0x244 ... 0x250:
730 s->window[(offset - 0x244) >> 2].blendeq = val;
738 case 0x280 ... 0x2AC:
739 s->ldi_cmd[(offset - 0x280) >> 2] = val;
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;
747 hw_error("s5pc1xx.lcd: bad write offset " TARGET_FMT_plx "\n",
752 static uint32_t s5pc1xx_lcd_read(void *opaque, target_phys_addr_t offset)
754 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
758 hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n", offset);
762 case 0x000 ... 0x008:
763 return s->vidcon[(offset - 0x000) >> 2];
766 case 0x010 ... 0x018:
767 return s->vidtcon[(offset - 0x010) >> 2];
768 case 0x020 ... 0x030:
769 return s->window[(offset - 0x020) >> 2].wincon;
772 case 0x040 ... 0x088:
773 w = (offset - 0x040) >> 4;
774 i = ((offset - 0x040) & 0xF) >> 2;
776 return s->window[w].vidosd[i];
778 if (w != 1 && w != 2) {
779 hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
782 return s->window[w].vidosd[i];
788 return s->window[w].vidosd[i];
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",
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",
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];
818 case 0x180 ... 0x190:
819 return s->window[(offset - 0x180) >> 2].winmap;
820 case 0x19C ... 0x1A0:
821 return s->wpalcon[(offset - 0x19C) >> 2];
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;
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];
850 hw_error("s5pc1xx.lcd: bad read offset " TARGET_FMT_plx "\n",
855 static CPUReadMemoryFunc *s5pc1xx_lcd_readfn[] = {
861 static CPUWriteMemoryFunc *s5pc1xx_lcd_writefn[] = {
867 static void s5pc1xx_update_resolution(S5pc1xxLcdState *s)
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) {
876 qemu_console_resize(s->console, width, height);
877 s->ifb = qemu_realloc(s->ifb, width * height * 7);
879 qemu_realloc(s->valid_line, (height >> 3) * sizeof(uint8_t));
881 qemu_realloc(s->valid_line_prev, (height >> 3) * sizeof(uint8_t));
882 memset(s->ifb, 0, width * height * 7);
887 /* Returns WxPAL for given window number WINDOW */
888 static uint32_t s5pc1xx_wxpal(S5pc1xxLcdState *s, int window)
892 return s->wpalcon[1] & 0x7;
894 return (s->wpalcon[1] >> 3) & 0x7;
896 return ((s->wpalcon[0] >> 8) & 0x6) | ((s->wpalcon[1] >> 6) & 0x1);
898 return ((s->wpalcon[0] >> 12) & 0x6) | ((s->wpalcon[1] >> 7) & 0x1);
900 return ((s->wpalcon[0] >> 16) & 0x6) | ((s->wpalcon[1] >> 8) & 0x1);
902 hw_error("s5pc1xx.lcd: incorrect window number %d\n", window);
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)
911 switch ((s->window[window].wincon >> 2) & 0xF) {
913 cfg->draw_line = draw_line1;
914 cfg->is_palletized = 1;
918 cfg->draw_line = draw_line2;
919 cfg->is_palletized = 1;
923 cfg->draw_line = draw_line4;
924 cfg->is_palletized = 1;
928 cfg->draw_line = draw_line8;
929 cfg->is_palletized = 1;
933 cfg->draw_line = draw_line8;
934 cfg->is_palletized = 0;
935 cfg->pixel_to_rgb = pixel_a232_to_rgb;
939 cfg->draw_line = draw_line16;
940 cfg->is_palletized = 0;
941 cfg->pixel_to_rgb = pixel_565_to_rgb;
945 cfg->draw_line = draw_line16;
946 cfg->is_palletized = 0;
947 cfg->pixel_to_rgb = pixel_a555_to_rgb;
951 cfg->draw_line = draw_line16;
952 cfg->is_palletized = 0;
953 cfg->pixel_to_rgb = pixel_1555_to_rgb;
957 cfg->draw_line = draw_line32;
958 cfg->is_palletized = 0;
959 cfg->pixel_to_rgb = pixel_666_to_rgb;
963 cfg->draw_line = draw_line32;
964 cfg->is_palletized = 0;
965 cfg->pixel_to_rgb = pixel_a665_to_rgb;
969 cfg->draw_line = draw_line32;
970 cfg->is_palletized = 0;
971 cfg->pixel_to_rgb = pixel_a666_to_rgb;
975 cfg->draw_line = draw_line32;
976 cfg->is_palletized = 0;
977 cfg->pixel_to_rgb = pixel_888_to_rgb;
981 cfg->draw_line = draw_line32;
982 cfg->is_palletized = 0;
983 cfg->pixel_to_rgb = pixel_a887_to_rgb;
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;
994 cfg->pixel_to_rgb = pixel_a888_to_rgb;
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;
1006 cfg->pixel_to_rgb = pixel_a444_to_rgb;
1011 cfg->draw_line = draw_line16;
1012 cfg->is_palletized = 0;
1013 cfg->pixel_to_rgb = pixel_555_to_rgb;
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
1030 static inline uint32_t unpack_by_4(uint32_t x)
1032 return ((x & 0xF00) << 12) | ((x & 0xF0) << 8) | ((x & 0xF) << 4);
1035 static coef_func *coef_decode(uint32_t x)
1045 return coef_one_minus_alphaa;
1049 return coef_one_minus_alphab;
1053 return coef_one_minus_a;
1057 return coef_one_minus_b;
1059 hw_error("s5pc1xx.lcd: illegal value\n");
1063 static inline void putpixel_by_bpp(DrawConfig *cfg, int bpp)
1067 cfg->put_pixel = put_pixel8;
1070 cfg->put_pixel = put_pixel15;
1073 cfg->put_pixel = put_pixel16;
1076 cfg->put_pixel = put_pixel24;
1079 cfg->put_pixel = put_pixel32;
1082 hw_error("s5pc1xx.lcd: unsupported BPP (%d)", bpp);
1086 static void s5pc1xx_lcd_update(void *opaque)
1088 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
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;
1100 int global_width, global_height;
1103 uint8_t is_first_window;
1105 if (!s || !s->console || !ds_get_bits_per_pixel(s->console)) {
1109 if (! (s->vidcon[0] & 2)) {
1113 memset(&cfg, 0, sizeof(cfg));
1115 s5pc1xx_update_resolution(s);
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;
1131 buf_id = (s->window[i].wincon >> 20) & 1;
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.
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;
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];
1165 tmp = line + lefttop_y;
1166 s->valid_line[tmp >> 3] &= ~(1 << (tmp & 0x7));
1168 dirty[0] = dirty[1] = 0;
1169 scanline += (s->window[i].buf_size & 0x1FFF) +
1170 ((s->window[i].buf_size >> 13) & 0x1FFF);
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,
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
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) {
1196 cfg.pixel_to_rgb = wxpal_to_rgb[tmp];
1198 cfg.fg_alpha_pix = 1;
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;
1208 cfg.fg_alpha[0] = s->window[i].vidw_alpha[0];
1209 cfg.fg_alpha[1] = s->window[i].vidw_alpha[1];
1212 unpack_by_4((s->window[i].vidosd[3] & 0xFFF000) >> 12) |
1213 (s->window[i].vidw_alpha[0] & 0xF0F0F);
1215 unpack_by_4(s->window[i].vidosd[3] & 0xFFF) |
1216 (s->window[i].vidw_alpha[0] & 0xF0F0F);
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) {
1230 cfg.blend = blend_colorkey;
1232 is_first_window = 0;
1233 /* At this point CFG is fully set up except WIDTH. We can proceed
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;
1242 ext_line_size = (width * cfg.bpp) >> 3;
1245 buf_id = (s->window[i].wincon >> 20) & 1;
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);
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)))) {
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);
1270 mapline += (s->window[i].buf_size & 0x1FFF) +
1271 ((s->window[i].buf_size >> 13) & 0x1FFF);
1273 cpu_physical_memory_unmap(startline, map_len, 0, 0);
1276 /* Last pass: copy resulting image to QEMU_CONSOLE. */
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);
1289 dpy_update(s->console, 0, 0, width, height);
1291 valid_line_tmp = s->valid_line;
1292 s->valid_line = s->valid_line_prev;
1293 s->valid_line_prev = valid_line_tmp;
1295 s->vidintcon[1] |= 2;
1296 s5pc1xx_lcd_update_irq(s);
1299 static void s5pc1xx_lcd_save(QEMUFile *f, void *opaque)
1301 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1302 int i, j, width, height;
1304 width = (s->vidtcon[2] & 0x7FF) + 1;
1305 height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
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);
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]);
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]);
1328 for (i = 0; i < 4; i++) {
1329 qemu_put_be32s(f, &s->i80ifcon[i]);
1332 for (i = 0; i < 12; i++) {
1333 qemu_put_be32s(f, &s->ldi_cmd[i]);
1336 //qemu_put_buffer(f, s->ifb, width * height * 7);
1338 /* Not saving valid_line because the whole screen will be redrawn anyway */
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);
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]);
1353 for (j = 0; j < 4; j++) {
1354 qemu_put_be32s(f, &s->window[i].vidosd[j]);
1357 for (j = 0; j < 256; j++) {
1358 qemu_put_be32s(f, &s->window[i].palette[j]);
1363 static int s5pc1xx_lcd_load(QEMUFile *f, void *opaque, int version_id)
1365 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1366 int i, j, width, height;
1368 if (version_id != 1) {
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);
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]);
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]);
1393 width = (s->vidtcon[2] & 0x7FF) + 1;
1394 height = ((s->vidtcon[2] >> 11) & 0x7FF) + 1;
1396 for (i = 0; i < 4; i++) {
1397 qemu_get_be32s(f, &s->i80ifcon[i]);
1400 for (i = 0; i < 12; i++) {
1401 qemu_get_be32s(f, &s->ldi_cmd[i]);
1404 //qemu_get_buffer(f, s->ifb, width * height * 7);
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);
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]);
1419 for (j = 0; j < 4; j++) {
1420 qemu_get_be32s(f, &s->window[i].vidosd[j]);
1423 for (j = 0; j < 256; j++) {
1424 qemu_get_be32s(f, &s->window[i].palette[j]);
1428 s5pc1xx_update_resolution(s);
1429 /* Redraw the whole screen */
1434 static void s5pc1xx_lcd_invalidate(void *opaque)
1436 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
1440 static void s5pc1xx_window_reset(S5pc1xxLcdWindow *s)
1442 memset(s, 0, sizeof(*s));
1446 static void s5pc1xx_lcd_reset(void *opaque)
1448 S5pc1xxLcdState *s = (S5pc1xxLcdState *)opaque;
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]);
1456 if (s->ifb != NULL) {
1460 if (s->valid_line != NULL) {
1461 qemu_free(s->valid_line);
1463 s->valid_line = NULL;
1464 if (s->valid_line_prev != NULL) {
1465 qemu_free(s->valid_line_prev);
1467 s->valid_line_prev = NULL;
1470 static int s5pc1xx_lcd_init(SysBusDevice *dev)
1473 S5pc1xxLcdState *s = FROM_SYSBUS(S5pc1xxLcdState, dev);
1476 s->valid_line = NULL;
1477 s->valid_line_prev = NULL;
1478 s5pc1xx_lcd_reset(s);
1480 sysbus_init_irq(dev, &s->irq[0]);
1481 sysbus_init_irq(dev, &s->irq[1]);
1482 sysbus_init_irq(dev, &s->irq[2]);
1485 cpu_register_io_memory(s5pc1xx_lcd_readfn, s5pc1xx_lcd_writefn, s,
1486 DEVICE_NATIVE_ENDIAN);
1487 sysbus_init_mmio(dev, 0x3800, iomemtype);
1489 s->console = graphic_console_init(s5pc1xx_lcd_update,
1490 s5pc1xx_lcd_invalidate, NULL, NULL, s);
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);
1499 static void s5pc1xx_lcd_register_devices(void)
1501 sysbus_register_dev("s5pc1xx.lcd", sizeof(S5pc1xxLcdState),
1505 device_init(s5pc1xx_lcd_register_devices)