vmstate: port hpet device
[qemu] / 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 "sun4m.h"
26 #include "console.h"
27 #include "pixel_ops.h"
28 #include "sysbus.h"
29 #include "qdev-addr.h"
30
31 #define MAXX 1024
32 #define MAXY 768
33 #define TCX_DAC_NREGS 16
34 #define TCX_THC_NREGS_8  0x081c
35 #define TCX_THC_NREGS_24 0x1000
36 #define TCX_TEC_NREGS    0x1000
37
38 typedef struct TCXState {
39     SysBusDevice busdev;
40     target_phys_addr_t addr;
41     DisplayState *ds;
42     uint8_t *vram;
43     uint32_t *vram24, *cplane;
44     ram_addr_t vram_offset, vram24_offset, cplane_offset;
45     uint32_t vram_size;
46     uint16_t width, height, depth;
47     uint8_t r[256], g[256], b[256];
48     uint32_t palette[256];
49     uint8_t dac_index, dac_state;
50 } TCXState;
51
52 static void tcx_screen_dump(void *opaque, const char *filename);
53 static void tcx24_screen_dump(void *opaque, const char *filename);
54
55 static void tcx_set_dirty(TCXState *s)
56 {
57     unsigned int i;
58
59     for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) {
60         cpu_physical_memory_set_dirty(s->vram_offset + i);
61     }
62 }
63
64 static void tcx24_set_dirty(TCXState *s)
65 {
66     unsigned int i;
67
68     for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) {
69         cpu_physical_memory_set_dirty(s->vram24_offset + i);
70         cpu_physical_memory_set_dirty(s->cplane_offset + i);
71     }
72 }
73
74 static void update_palette_entries(TCXState *s, int start, int end)
75 {
76     int i;
77     for(i = start; i < end; i++) {
78         switch(ds_get_bits_per_pixel(s->ds)) {
79         default:
80         case 8:
81             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
82             break;
83         case 15:
84             s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
85             break;
86         case 16:
87             s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
88             break;
89         case 32:
90             if (is_surface_bgr(s->ds->surface))
91                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
92             else
93                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
94             break;
95         }
96     }
97     if (s->depth == 24) {
98         tcx24_set_dirty(s);
99     } else {
100         tcx_set_dirty(s);
101     }
102 }
103
104 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
105                             const uint8_t *s, int width)
106 {
107     int x;
108     uint8_t val;
109     uint32_t *p = (uint32_t *)d;
110
111     for(x = 0; x < width; x++) {
112         val = *s++;
113         *p++ = s1->palette[val];
114     }
115 }
116
117 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
118                             const uint8_t *s, int width)
119 {
120     int x;
121     uint8_t val;
122     uint16_t *p = (uint16_t *)d;
123
124     for(x = 0; x < width; x++) {
125         val = *s++;
126         *p++ = s1->palette[val];
127     }
128 }
129
130 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
131                            const uint8_t *s, int width)
132 {
133     int x;
134     uint8_t val;
135
136     for(x = 0; x < width; x++) {
137         val = *s++;
138         *d++ = s1->palette[val];
139     }
140 }
141
142 /*
143   XXX Could be much more optimal:
144   * detect if line/page/whole screen is in 24 bit mode
145   * if destination is also BGR, use memcpy
146   */
147 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
148                                      const uint8_t *s, int width,
149                                      const uint32_t *cplane,
150                                      const uint32_t *s24)
151 {
152     int x, bgr, r, g, b;
153     uint8_t val, *p8;
154     uint32_t *p = (uint32_t *)d;
155     uint32_t dval;
156
157     bgr = is_surface_bgr(s1->ds->surface);
158     for(x = 0; x < width; x++, s++, s24++) {
159         if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
160             // 24-bit direct, BGR order
161             p8 = (uint8_t *)s24;
162             p8++;
163             b = *p8++;
164             g = *p8++;
165             r = *p8++;
166             if (bgr)
167                 dval = rgb_to_pixel32bgr(r, g, b);
168             else
169                 dval = rgb_to_pixel32(r, g, b);
170         } else {
171             val = *s;
172             dval = s1->palette[val];
173         }
174         *p++ = dval;
175     }
176 }
177
178 static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
179                               ram_addr_t cpage)
180 {
181     int ret;
182     unsigned int off;
183
184     ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
185     for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
186         ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
187         ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
188     }
189     return ret;
190 }
191
192 static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
193                                ram_addr_t page_max, ram_addr_t page24,
194                               ram_addr_t cpage)
195 {
196     cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
197                                     VGA_DIRTY_FLAG);
198     page_min -= ts->vram_offset;
199     page_max -= ts->vram_offset;
200     cpu_physical_memory_reset_dirty(page24 + page_min * 4,
201                                     page24 + page_max * 4 + TARGET_PAGE_SIZE,
202                                     VGA_DIRTY_FLAG);
203     cpu_physical_memory_reset_dirty(cpage + page_min * 4,
204                                     cpage + page_max * 4 + TARGET_PAGE_SIZE,
205                                     VGA_DIRTY_FLAG);
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 = ts->vram_offset;
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 (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
247             if (y_start < 0)
248                 y_start = y;
249             if (page < page_min)
250                 page_min = page;
251             if (page > page_max)
252                 page_max = page;
253             f(ts, d, s, ts->width);
254             d += dd;
255             s += ds;
256             f(ts, d, s, ts->width);
257             d += dd;
258             s += ds;
259             f(ts, d, s, ts->width);
260             d += dd;
261             s += ds;
262             f(ts, d, s, ts->width);
263             d += dd;
264             s += ds;
265         } else {
266             if (y_start >= 0) {
267                 /* flush to display */
268                 dpy_update(ts->ds, 0, y_start,
269                            ts->width, y - y_start);
270                 y_start = -1;
271             }
272             d += dd * 4;
273             s += ds * 4;
274         }
275     }
276     if (y_start >= 0) {
277         /* flush to display */
278         dpy_update(ts->ds, 0, y_start,
279                    ts->width, y - y_start);
280     }
281     /* reset modified pages */
282     if (page_max >= page_min) {
283         cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
284                                         VGA_DIRTY_FLAG);
285     }
286 }
287
288 static void tcx24_update_display(void *opaque)
289 {
290     TCXState *ts = opaque;
291     ram_addr_t page, page_min, page_max, cpage, page24;
292     int y, y_start, dd, ds;
293     uint8_t *d, *s;
294     uint32_t *cptr, *s24;
295
296     if (ds_get_bits_per_pixel(ts->ds) != 32)
297             return;
298     page = ts->vram_offset;
299     page24 = ts->vram24_offset;
300     cpage = ts->cplane_offset;
301     y_start = -1;
302     page_min = -1;
303     page_max = 0;
304     d = ds_get_data(ts->ds);
305     s = ts->vram;
306     s24 = ts->vram24;
307     cptr = ts->cplane;
308     dd = ds_get_linesize(ts->ds);
309     ds = 1024;
310
311     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
312             page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
313         if (check_dirty(page, page24, cpage)) {
314             if (y_start < 0)
315                 y_start = y;
316             if (page < page_min)
317                 page_min = page;
318             if (page > page_max)
319                 page_max = page;
320             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
321             d += dd;
322             s += ds;
323             cptr += ds;
324             s24 += ds;
325             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
326             d += dd;
327             s += ds;
328             cptr += ds;
329             s24 += ds;
330             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
331             d += dd;
332             s += ds;
333             cptr += ds;
334             s24 += ds;
335             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
336             d += dd;
337             s += ds;
338             cptr += ds;
339             s24 += ds;
340         } else {
341             if (y_start >= 0) {
342                 /* flush to display */
343                 dpy_update(ts->ds, 0, y_start,
344                            ts->width, y - y_start);
345                 y_start = -1;
346             }
347             d += dd * 4;
348             s += ds * 4;
349             cptr += ds * 4;
350             s24 += ds * 4;
351         }
352     }
353     if (y_start >= 0) {
354         /* flush to display */
355         dpy_update(ts->ds, 0, y_start,
356                    ts->width, y - y_start);
357     }
358     /* reset modified pages */
359     if (page_max >= page_min) {
360         reset_dirty(ts, page_min, page_max, page24, cpage);
361     }
362 }
363
364 static void tcx_invalidate_display(void *opaque)
365 {
366     TCXState *s = opaque;
367
368     tcx_set_dirty(s);
369     qemu_console_resize(s->ds, s->width, s->height);
370 }
371
372 static void tcx24_invalidate_display(void *opaque)
373 {
374     TCXState *s = opaque;
375
376     tcx_set_dirty(s);
377     tcx24_set_dirty(s);
378     qemu_console_resize(s->ds, s->width, s->height);
379 }
380
381 static int vmstate_tcx_post_load(void *opaque)
382 {
383     TCXState *s = opaque;
384
385     update_palette_entries(s, 0, 256);
386     if (s->depth == 24) {
387         tcx24_set_dirty(s);
388     } else {
389         tcx_set_dirty(s);
390     }
391
392     return 0;
393 }
394
395 static const VMStateDescription vmstate_tcx = {
396     .name ="tcx",
397     .version_id = 4,
398     .minimum_version_id = 4,
399     .minimum_version_id_old = 4,
400     .post_load = vmstate_tcx_post_load,
401     .fields      = (VMStateField []) {
402         VMSTATE_UINT16(height, TCXState),
403         VMSTATE_UINT16(width, TCXState),
404         VMSTATE_UINT16(depth, TCXState),
405         VMSTATE_BUFFER(r, TCXState),
406         VMSTATE_BUFFER(g, TCXState),
407         VMSTATE_BUFFER(b, TCXState),
408         VMSTATE_UINT8(dac_index, TCXState),
409         VMSTATE_UINT8(dac_state, TCXState),
410         VMSTATE_END_OF_LIST()
411     }
412 };
413
414 static void tcx_reset(void *opaque)
415 {
416     TCXState *s = opaque;
417
418     /* Initialize palette */
419     memset(s->r, 0, 256);
420     memset(s->g, 0, 256);
421     memset(s->b, 0, 256);
422     s->r[255] = s->g[255] = s->b[255] = 255;
423     update_palette_entries(s, 0, 256);
424     memset(s->vram, 0, MAXX*MAXY);
425     cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
426                                     MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
427     s->dac_index = 0;
428     s->dac_state = 0;
429 }
430
431 static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
432 {
433     return 0;
434 }
435
436 static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
437 {
438     TCXState *s = opaque;
439
440     switch (addr) {
441     case 0:
442         s->dac_index = val >> 24;
443         s->dac_state = 0;
444         break;
445     case 4:
446         switch (s->dac_state) {
447         case 0:
448             s->r[s->dac_index] = val >> 24;
449             update_palette_entries(s, s->dac_index, s->dac_index + 1);
450             s->dac_state++;
451             break;
452         case 1:
453             s->g[s->dac_index] = val >> 24;
454             update_palette_entries(s, s->dac_index, s->dac_index + 1);
455             s->dac_state++;
456             break;
457         case 2:
458             s->b[s->dac_index] = val >> 24;
459             update_palette_entries(s, s->dac_index, s->dac_index + 1);
460             s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
461         default:
462             s->dac_state = 0;
463             break;
464         }
465         break;
466     default:
467         break;
468     }
469     return;
470 }
471
472 static CPUReadMemoryFunc * const tcx_dac_read[3] = {
473     NULL,
474     NULL,
475     tcx_dac_readl,
476 };
477
478 static CPUWriteMemoryFunc * const tcx_dac_write[3] = {
479     NULL,
480     NULL,
481     tcx_dac_writel,
482 };
483
484 static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
485 {
486     return 0;
487 }
488
489 static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
490                              uint32_t val)
491 {
492 }
493
494 static CPUReadMemoryFunc * const tcx_dummy_read[3] = {
495     NULL,
496     NULL,
497     tcx_dummy_readl,
498 };
499
500 static CPUWriteMemoryFunc * const tcx_dummy_write[3] = {
501     NULL,
502     NULL,
503     tcx_dummy_writel,
504 };
505
506 static int tcx_init1(SysBusDevice *dev)
507 {
508     TCXState *s = FROM_SYSBUS(TCXState, dev);
509     int io_memory, dummy_memory;
510     ram_addr_t vram_offset;
511     int size;
512     uint8_t *vram_base;
513
514     vram_offset = qemu_ram_alloc(s->vram_size * (1 + 4 + 4));
515     vram_base = qemu_get_ram_ptr(vram_offset);
516     s->vram_offset = vram_offset;
517
518     /* 8-bit plane */
519     s->vram = vram_base;
520     size = s->vram_size;
521     sysbus_init_mmio(dev, size, s->vram_offset);
522     vram_offset += size;
523     vram_base += size;
524
525     /* DAC */
526     io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s);
527     sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
528
529     /* TEC (dummy) */
530     dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
531                                           s);
532     sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
533     /* THC: NetBSD writes here even with 8-bit display: dummy */
534     sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
535
536     if (s->depth == 24) {
537         /* 24-bit plane */
538         size = s->vram_size * 4;
539         s->vram24 = (uint32_t *)vram_base;
540         s->vram24_offset = vram_offset;
541         sysbus_init_mmio(dev, size, vram_offset);
542         vram_offset += size;
543         vram_base += size;
544
545         /* Control plane */
546         size = s->vram_size * 4;
547         s->cplane = (uint32_t *)vram_base;
548         s->cplane_offset = vram_offset;
549         sysbus_init_mmio(dev, size, vram_offset);
550
551         s->ds = graphic_console_init(tcx24_update_display,
552                                      tcx24_invalidate_display,
553                                      tcx24_screen_dump, NULL, s);
554     } else {
555         /* THC 8 bit (dummy) */
556         sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
557
558         s->ds = graphic_console_init(tcx_update_display,
559                                      tcx_invalidate_display,
560                                      tcx_screen_dump, NULL, s);
561     }
562
563     tcx_reset(s);
564     qemu_console_resize(s->ds, s->width, s->height);
565     return 0;
566 }
567
568 static void tcx_screen_dump(void *opaque, const char *filename)
569 {
570     TCXState *s = opaque;
571     FILE *f;
572     uint8_t *d, *d1, v;
573     int y, x;
574
575     f = fopen(filename, "wb");
576     if (!f)
577         return;
578     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
579     d1 = s->vram;
580     for(y = 0; y < s->height; y++) {
581         d = d1;
582         for(x = 0; x < s->width; x++) {
583             v = *d;
584             fputc(s->r[v], f);
585             fputc(s->g[v], f);
586             fputc(s->b[v], f);
587             d++;
588         }
589         d1 += MAXX;
590     }
591     fclose(f);
592     return;
593 }
594
595 static void tcx24_screen_dump(void *opaque, const char *filename)
596 {
597     TCXState *s = opaque;
598     FILE *f;
599     uint8_t *d, *d1, v;
600     uint32_t *s24, *cptr, dval;
601     int y, x;
602
603     f = fopen(filename, "wb");
604     if (!f)
605         return;
606     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
607     d1 = s->vram;
608     s24 = s->vram24;
609     cptr = s->cplane;
610     for(y = 0; y < s->height; y++) {
611         d = d1;
612         for(x = 0; x < s->width; x++, d++, s24++) {
613             if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
614                 dval = *s24 & 0x00ffffff;
615                 fputc((dval >> 16) & 0xff, f);
616                 fputc((dval >> 8) & 0xff, f);
617                 fputc(dval & 0xff, f);
618             } else {
619                 v = *d;
620                 fputc(s->r[v], f);
621                 fputc(s->g[v], f);
622                 fputc(s->b[v], f);
623             }
624         }
625         d1 += MAXX;
626     }
627     fclose(f);
628     return;
629 }
630
631 static SysBusDeviceInfo tcx_info = {
632     .init = tcx_init1,
633     .qdev.name  = "SUNW,tcx",
634     .qdev.size  = sizeof(TCXState),
635     .qdev.reset = tcx_reset,
636     .qdev.vmsd  = &vmstate_tcx,
637     .qdev.props = (Property[]) {
638         DEFINE_PROP_TADDR("addr",      TCXState, addr,      -1),
639         DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
640         DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
641         DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
642         DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
643         DEFINE_PROP_END_OF_LIST(),
644     }
645 };
646
647 static void tcx_register_devices(void)
648 {
649     sysbus_register_withprop(&tcx_info);
650 }
651
652 device_init(tcx_register_devices)