sync with tizen_2.2
[sdk/emulator/qemu.git] / hw / tcx.c
1 /*
2  * QEMU TCX Frame buffer
3  *
4  * Copyright (c) 2003-2005 Fabrice Bellard
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "console.h"
26 #include "pixel_ops.h"
27 #include "sysbus.h"
28 #include "qdev-addr.h"
29
30 #define MAXX 1024
31 #define MAXY 768
32 #define TCX_DAC_NREGS 16
33 #define TCX_THC_NREGS_8  0x081c
34 #define TCX_THC_NREGS_24 0x1000
35 #define TCX_TEC_NREGS    0x1000
36
37 typedef struct TCXState {
38     SysBusDevice busdev;
39     target_phys_addr_t addr;
40     DisplayState *ds;
41     uint8_t *vram;
42     uint32_t *vram24, *cplane;
43     MemoryRegion vram_mem;
44     MemoryRegion vram_8bit;
45     MemoryRegion vram_24bit;
46     MemoryRegion vram_cplane;
47     MemoryRegion dac;
48     MemoryRegion tec;
49     MemoryRegion thc24;
50     MemoryRegion thc8;
51     ram_addr_t vram24_offset, cplane_offset;
52     uint32_t vram_size;
53     uint32_t palette[256];
54     uint8_t r[256], g[256], b[256];
55     uint16_t width, height, depth;
56     uint8_t dac_index, dac_state;
57 } TCXState;
58
59 static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch);
60 static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch);
61
62 static void tcx_set_dirty(TCXState *s)
63 {
64     memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
65 }
66
67 static void tcx24_set_dirty(TCXState *s)
68 {
69     memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4);
70     memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4);
71 }
72
73 static void update_palette_entries(TCXState *s, int start, int end)
74 {
75     int i;
76     for(i = start; i < end; i++) {
77         switch(ds_get_bits_per_pixel(s->ds)) {
78         default:
79         case 8:
80             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
81             break;
82         case 15:
83             s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
84             break;
85         case 16:
86             s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
87             break;
88         case 32:
89             if (is_surface_bgr(s->ds->surface))
90                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
91             else
92                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
93             break;
94         }
95     }
96     if (s->depth == 24) {
97         tcx24_set_dirty(s);
98     } else {
99         tcx_set_dirty(s);
100     }
101 }
102
103 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
104                             const uint8_t *s, int width)
105 {
106     int x;
107     uint8_t val;
108     uint32_t *p = (uint32_t *)d;
109
110     for(x = 0; x < width; x++) {
111         val = *s++;
112         *p++ = s1->palette[val];
113     }
114 }
115
116 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
117                             const uint8_t *s, int width)
118 {
119     int x;
120     uint8_t val;
121     uint16_t *p = (uint16_t *)d;
122
123     for(x = 0; x < width; x++) {
124         val = *s++;
125         *p++ = s1->palette[val];
126     }
127 }
128
129 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
130                            const uint8_t *s, int width)
131 {
132     int x;
133     uint8_t val;
134
135     for(x = 0; x < width; x++) {
136         val = *s++;
137         *d++ = s1->palette[val];
138     }
139 }
140
141 /*
142   XXX Could be much more optimal:
143   * detect if line/page/whole screen is in 24 bit mode
144   * if destination is also BGR, use memcpy
145   */
146 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
147                                      const uint8_t *s, int width,
148                                      const uint32_t *cplane,
149                                      const uint32_t *s24)
150 {
151     int x, bgr, r, g, b;
152     uint8_t val, *p8;
153     uint32_t *p = (uint32_t *)d;
154     uint32_t dval;
155
156     bgr = is_surface_bgr(s1->ds->surface);
157     for(x = 0; x < width; x++, s++, s24++) {
158         if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
159             // 24-bit direct, BGR order
160             p8 = (uint8_t *)s24;
161             p8++;
162             b = *p8++;
163             g = *p8++;
164             r = *p8;
165             if (bgr)
166                 dval = rgb_to_pixel32bgr(r, g, b);
167             else
168                 dval = rgb_to_pixel32(r, g, b);
169         } else {
170             val = *s;
171             dval = s1->palette[val];
172         }
173         *p++ = dval;
174     }
175 }
176
177 static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24,
178                               ram_addr_t cpage)
179 {
180     int ret;
181
182     ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE,
183                                   DIRTY_MEMORY_VGA);
184     ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4,
185                                    DIRTY_MEMORY_VGA);
186     ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4,
187                                    DIRTY_MEMORY_VGA);
188     return ret;
189 }
190
191 static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
192                                ram_addr_t page_max, ram_addr_t page24,
193                               ram_addr_t cpage)
194 {
195     memory_region_reset_dirty(&ts->vram_mem,
196                               page_min, page_max + TARGET_PAGE_SIZE,
197                               DIRTY_MEMORY_VGA);
198     memory_region_reset_dirty(&ts->vram_mem,
199                               page24 + page_min * 4,
200                               page24 + page_max * 4 + TARGET_PAGE_SIZE,
201                               DIRTY_MEMORY_VGA);
202     memory_region_reset_dirty(&ts->vram_mem,
203                               cpage + page_min * 4,
204                               cpage + page_max * 4 + TARGET_PAGE_SIZE,
205                               DIRTY_MEMORY_VGA);
206 }
207
208 /* Fixed line length 1024 allows us to do nice tricks not possible on
209    VGA... */
210 static void tcx_update_display(void *opaque)
211 {
212     TCXState *ts = opaque;
213     ram_addr_t page, page_min, page_max;
214     int y, y_start, dd, ds;
215     uint8_t *d, *s;
216     void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
217
218     if (ds_get_bits_per_pixel(ts->ds) == 0)
219         return;
220     page = 0;
221     y_start = -1;
222     page_min = -1;
223     page_max = 0;
224     d = ds_get_data(ts->ds);
225     s = ts->vram;
226     dd = ds_get_linesize(ts->ds);
227     ds = 1024;
228
229     switch (ds_get_bits_per_pixel(ts->ds)) {
230     case 32:
231         f = tcx_draw_line32;
232         break;
233     case 15:
234     case 16:
235         f = tcx_draw_line16;
236         break;
237     default:
238     case 8:
239         f = tcx_draw_line8;
240         break;
241     case 0:
242         return;
243     }
244
245     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
246         if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
247                                     DIRTY_MEMORY_VGA)) {
248             if (y_start < 0)
249                 y_start = y;
250             if (page < page_min)
251                 page_min = page;
252             if (page > page_max)
253                 page_max = page;
254             f(ts, d, s, ts->width);
255             d += dd;
256             s += ds;
257             f(ts, d, s, ts->width);
258             d += dd;
259             s += ds;
260             f(ts, d, s, ts->width);
261             d += dd;
262             s += ds;
263             f(ts, d, s, ts->width);
264             d += dd;
265             s += ds;
266         } else {
267             if (y_start >= 0) {
268                 /* flush to display */
269                 dpy_update(ts->ds, 0, y_start,
270                            ts->width, y - y_start);
271                 y_start = -1;
272             }
273             d += dd * 4;
274             s += ds * 4;
275         }
276     }
277     if (y_start >= 0) {
278         /* flush to display */
279         dpy_update(ts->ds, 0, y_start,
280                    ts->width, y - y_start);
281     }
282     /* reset modified pages */
283     if (page_max >= page_min) {
284         memory_region_reset_dirty(&ts->vram_mem,
285                                   page_min, page_max + TARGET_PAGE_SIZE,
286                                   DIRTY_MEMORY_VGA);
287     }
288 }
289
290 static void tcx24_update_display(void *opaque)
291 {
292     TCXState *ts = opaque;
293     ram_addr_t page, page_min, page_max, cpage, page24;
294     int y, y_start, dd, ds;
295     uint8_t *d, *s;
296     uint32_t *cptr, *s24;
297
298     if (ds_get_bits_per_pixel(ts->ds) != 32)
299             return;
300     page = 0;
301     page24 = ts->vram24_offset;
302     cpage = ts->cplane_offset;
303     y_start = -1;
304     page_min = -1;
305     page_max = 0;
306     d = ds_get_data(ts->ds);
307     s = ts->vram;
308     s24 = ts->vram24;
309     cptr = ts->cplane;
310     dd = ds_get_linesize(ts->ds);
311     ds = 1024;
312
313     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
314             page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
315         if (check_dirty(ts, page, page24, cpage)) {
316             if (y_start < 0)
317                 y_start = y;
318             if (page < page_min)
319                 page_min = page;
320             if (page > page_max)
321                 page_max = page;
322             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
323             d += dd;
324             s += ds;
325             cptr += ds;
326             s24 += ds;
327             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
328             d += dd;
329             s += ds;
330             cptr += ds;
331             s24 += ds;
332             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
333             d += dd;
334             s += ds;
335             cptr += ds;
336             s24 += ds;
337             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
338             d += dd;
339             s += ds;
340             cptr += ds;
341             s24 += ds;
342         } else {
343             if (y_start >= 0) {
344                 /* flush to display */
345                 dpy_update(ts->ds, 0, y_start,
346                            ts->width, y - y_start);
347                 y_start = -1;
348             }
349             d += dd * 4;
350             s += ds * 4;
351             cptr += ds * 4;
352             s24 += ds * 4;
353         }
354     }
355     if (y_start >= 0) {
356         /* flush to display */
357         dpy_update(ts->ds, 0, y_start,
358                    ts->width, y - y_start);
359     }
360     /* reset modified pages */
361     if (page_max >= page_min) {
362         reset_dirty(ts, page_min, page_max, page24, cpage);
363     }
364 }
365
366 static void tcx_invalidate_display(void *opaque)
367 {
368     TCXState *s = opaque;
369
370     tcx_set_dirty(s);
371     qemu_console_resize(s->ds, s->width, s->height);
372 }
373
374 static void tcx24_invalidate_display(void *opaque)
375 {
376     TCXState *s = opaque;
377
378     tcx_set_dirty(s);
379     tcx24_set_dirty(s);
380     qemu_console_resize(s->ds, s->width, s->height);
381 }
382
383 static int vmstate_tcx_post_load(void *opaque, int version_id)
384 {
385     TCXState *s = opaque;
386
387     update_palette_entries(s, 0, 256);
388     if (s->depth == 24) {
389         tcx24_set_dirty(s);
390     } else {
391         tcx_set_dirty(s);
392     }
393
394     return 0;
395 }
396
397 static const VMStateDescription vmstate_tcx = {
398     .name ="tcx",
399     .version_id = 4,
400     .minimum_version_id = 4,
401     .minimum_version_id_old = 4,
402     .post_load = vmstate_tcx_post_load,
403     .fields      = (VMStateField []) {
404         VMSTATE_UINT16(height, TCXState),
405         VMSTATE_UINT16(width, TCXState),
406         VMSTATE_UINT16(depth, TCXState),
407         VMSTATE_BUFFER(r, TCXState),
408         VMSTATE_BUFFER(g, TCXState),
409         VMSTATE_BUFFER(b, TCXState),
410         VMSTATE_UINT8(dac_index, TCXState),
411         VMSTATE_UINT8(dac_state, TCXState),
412         VMSTATE_END_OF_LIST()
413     }
414 };
415
416 static void tcx_reset(DeviceState *d)
417 {
418     TCXState *s = container_of(d, TCXState, busdev.qdev);
419
420     /* Initialize palette */
421     memset(s->r, 0, 256);
422     memset(s->g, 0, 256);
423     memset(s->b, 0, 256);
424     s->r[255] = s->g[255] = s->b[255] = 255;
425     update_palette_entries(s, 0, 256);
426     memset(s->vram, 0, MAXX*MAXY);
427     memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
428                               DIRTY_MEMORY_VGA);
429     s->dac_index = 0;
430     s->dac_state = 0;
431 }
432
433 static uint64_t tcx_dac_readl(void *opaque, target_phys_addr_t addr,
434                               unsigned size)
435 {
436     return 0;
437 }
438
439 static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint64_t val,
440                            unsigned size)
441 {
442     TCXState *s = opaque;
443
444     switch (addr) {
445     case 0:
446         s->dac_index = val >> 24;
447         s->dac_state = 0;
448         break;
449     case 4:
450         switch (s->dac_state) {
451         case 0:
452             s->r[s->dac_index] = val >> 24;
453             update_palette_entries(s, s->dac_index, s->dac_index + 1);
454             s->dac_state++;
455             break;
456         case 1:
457             s->g[s->dac_index] = val >> 24;
458             update_palette_entries(s, s->dac_index, s->dac_index + 1);
459             s->dac_state++;
460             break;
461         case 2:
462             s->b[s->dac_index] = val >> 24;
463             update_palette_entries(s, s->dac_index, s->dac_index + 1);
464             s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
465         default:
466             s->dac_state = 0;
467             break;
468         }
469         break;
470     default:
471         break;
472     }
473     return;
474 }
475
476 static const MemoryRegionOps tcx_dac_ops = {
477     .read = tcx_dac_readl,
478     .write = tcx_dac_writel,
479     .endianness = DEVICE_NATIVE_ENDIAN,
480     .valid = {
481         .min_access_size = 4,
482         .max_access_size = 4,
483     },
484 };
485
486 static uint64_t dummy_readl(void *opaque, target_phys_addr_t addr,
487                             unsigned size)
488 {
489     return 0;
490 }
491
492 static void dummy_writel(void *opaque, target_phys_addr_t addr,
493                          uint64_t val, unsigned size)
494 {
495 }
496
497 static const MemoryRegionOps dummy_ops = {
498     .read = dummy_readl,
499     .write = dummy_writel,
500     .endianness = DEVICE_NATIVE_ENDIAN,
501     .valid = {
502         .min_access_size = 4,
503         .max_access_size = 4,
504     },
505 };
506
507 static int tcx_init1(SysBusDevice *dev)
508 {
509     TCXState *s = FROM_SYSBUS(TCXState, dev);
510     ram_addr_t vram_offset = 0;
511     int size;
512     uint8_t *vram_base;
513
514     memory_region_init_ram(&s->vram_mem, "tcx.vram",
515                            s->vram_size * (1 + 4 + 4));
516     vmstate_register_ram_global(&s->vram_mem);
517     vram_base = memory_region_get_ram_ptr(&s->vram_mem);
518
519     /* 8-bit plane */
520     s->vram = vram_base;
521     size = s->vram_size;
522     memory_region_init_alias(&s->vram_8bit, "tcx.vram.8bit",
523                              &s->vram_mem, vram_offset, size);
524     sysbus_init_mmio(dev, &s->vram_8bit);
525     vram_offset += size;
526     vram_base += size;
527
528     /* DAC */
529     memory_region_init_io(&s->dac, &tcx_dac_ops, s, "tcx.dac", TCX_DAC_NREGS);
530     sysbus_init_mmio(dev, &s->dac);
531
532     /* TEC (dummy) */
533     memory_region_init_io(&s->tec, &dummy_ops, s, "tcx.tec", TCX_TEC_NREGS);
534     sysbus_init_mmio(dev, &s->tec);
535     /* THC: NetBSD writes here even with 8-bit display: dummy */
536     memory_region_init_io(&s->thc24, &dummy_ops, s, "tcx.thc24",
537                           TCX_THC_NREGS_24);
538     sysbus_init_mmio(dev, &s->thc24);
539
540     if (s->depth == 24) {
541         /* 24-bit plane */
542         size = s->vram_size * 4;
543         s->vram24 = (uint32_t *)vram_base;
544         s->vram24_offset = vram_offset;
545         memory_region_init_alias(&s->vram_24bit, "tcx.vram.24bit",
546                                  &s->vram_mem, vram_offset, size);
547         sysbus_init_mmio(dev, &s->vram_24bit);
548         vram_offset += size;
549         vram_base += size;
550
551         /* Control plane */
552         size = s->vram_size * 4;
553         s->cplane = (uint32_t *)vram_base;
554         s->cplane_offset = vram_offset;
555         memory_region_init_alias(&s->vram_cplane, "tcx.vram.cplane",
556                                  &s->vram_mem, vram_offset, size);
557         sysbus_init_mmio(dev, &s->vram_cplane);
558
559         s->ds = graphic_console_init(tcx24_update_display,
560                                      tcx24_invalidate_display,
561                                      tcx24_screen_dump, NULL, s);
562     } else {
563         /* THC 8 bit (dummy) */
564         memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
565                               TCX_THC_NREGS_8);
566         sysbus_init_mmio(dev, &s->thc8);
567
568         s->ds = graphic_console_init(tcx_update_display,
569                                      tcx_invalidate_display,
570                                      tcx_screen_dump, NULL, s);
571     }
572
573     qemu_console_resize(s->ds, s->width, s->height);
574     return 0;
575 }
576
577 static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch)
578 {
579     TCXState *s = opaque;
580     FILE *f;
581     uint8_t *d, *d1, v;
582     int y, x;
583
584     f = fopen(filename, "wb");
585     if (!f)
586         return;
587     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
588     d1 = s->vram;
589     for(y = 0; y < s->height; y++) {
590         d = d1;
591         for(x = 0; x < s->width; x++) {
592             v = *d;
593             fputc(s->r[v], f);
594             fputc(s->g[v], f);
595             fputc(s->b[v], f);
596             d++;
597         }
598         d1 += MAXX;
599     }
600     fclose(f);
601     return;
602 }
603
604 static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch)
605 {
606     TCXState *s = opaque;
607     FILE *f;
608     uint8_t *d, *d1, v;
609     uint32_t *s24, *cptr, dval;
610     int y, x;
611
612     f = fopen(filename, "wb");
613     if (!f)
614         return;
615     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
616     d1 = s->vram;
617     s24 = s->vram24;
618     cptr = s->cplane;
619     for(y = 0; y < s->height; y++) {
620         d = d1;
621         for(x = 0; x < s->width; x++, d++, s24++) {
622             if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
623                 dval = *s24 & 0x00ffffff;
624                 fputc((dval >> 16) & 0xff, f);
625                 fputc((dval >> 8) & 0xff, f);
626                 fputc(dval & 0xff, f);
627             } else {
628                 v = *d;
629                 fputc(s->r[v], f);
630                 fputc(s->g[v], f);
631                 fputc(s->b[v], f);
632             }
633         }
634         d1 += MAXX;
635     }
636     fclose(f);
637     return;
638 }
639
640 static Property tcx_properties[] = {
641     DEFINE_PROP_TADDR("addr",      TCXState, addr,      -1),
642     DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
643     DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
644     DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
645     DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
646     DEFINE_PROP_END_OF_LIST(),
647 };
648
649 static void tcx_class_init(ObjectClass *klass, void *data)
650 {
651     DeviceClass *dc = DEVICE_CLASS(klass);
652     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
653
654     k->init = tcx_init1;
655     dc->reset = tcx_reset;
656     dc->vmsd = &vmstate_tcx;
657     dc->props = tcx_properties;
658 }
659
660 static TypeInfo tcx_info = {
661     .name          = "SUNW,tcx",
662     .parent        = TYPE_SYS_BUS_DEVICE,
663     .instance_size = sizeof(TCXState),
664     .class_init    = tcx_class_init,
665 };
666
667 static void tcx_register_types(void)
668 {
669     type_register_static(&tcx_info);
670 }
671
672 type_init(tcx_register_types)