xref: /openbmc/qemu/hw/display/tcx.c (revision 80adf54e)
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 "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "qemu-common.h"
28 #include "ui/console.h"
29 #include "ui/pixel_ops.h"
30 #include "hw/loader.h"
31 #include "hw/sysbus.h"
32 #include "qemu/error-report.h"
33 
34 #define TCX_ROM_FILE "QEMU,tcx.bin"
35 #define FCODE_MAX_ROM_SIZE 0x10000
36 
37 #define MAXX 1024
38 #define MAXY 768
39 #define TCX_DAC_NREGS    16
40 #define TCX_THC_NREGS    0x1000
41 #define TCX_DHC_NREGS    0x4000
42 #define TCX_TEC_NREGS    0x1000
43 #define TCX_ALT_NREGS    0x8000
44 #define TCX_STIP_NREGS   0x800000
45 #define TCX_BLIT_NREGS   0x800000
46 #define TCX_RSTIP_NREGS  0x800000
47 #define TCX_RBLIT_NREGS  0x800000
48 
49 #define TCX_THC_MISC     0x818
50 #define TCX_THC_CURSXY   0x8fc
51 #define TCX_THC_CURSMASK 0x900
52 #define TCX_THC_CURSBITS 0x980
53 
54 #define TYPE_TCX "SUNW,tcx"
55 #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)
56 
57 typedef struct TCXState {
58     SysBusDevice parent_obj;
59 
60     QemuConsole *con;
61     qemu_irq irq;
62     uint8_t *vram;
63     uint32_t *vram24, *cplane;
64     hwaddr prom_addr;
65     MemoryRegion rom;
66     MemoryRegion vram_mem;
67     MemoryRegion vram_8bit;
68     MemoryRegion vram_24bit;
69     MemoryRegion stip;
70     MemoryRegion blit;
71     MemoryRegion vram_cplane;
72     MemoryRegion rstip;
73     MemoryRegion rblit;
74     MemoryRegion tec;
75     MemoryRegion dac;
76     MemoryRegion thc;
77     MemoryRegion dhc;
78     MemoryRegion alt;
79     MemoryRegion thc24;
80 
81     ram_addr_t vram24_offset, cplane_offset;
82     uint32_t tmpblit;
83     uint32_t vram_size;
84     uint32_t palette[260];
85     uint8_t r[260], g[260], b[260];
86     uint16_t width, height, depth;
87     uint8_t dac_index, dac_state;
88     uint32_t thcmisc;
89     uint32_t cursmask[32];
90     uint32_t cursbits[32];
91     uint16_t cursx;
92     uint16_t cursy;
93 } TCXState;
94 
95 static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
96 {
97     memory_region_set_dirty(&s->vram_mem, addr, len);
98 
99     if (s->depth == 24) {
100         memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
101                                 len * 4);
102         memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
103                                 len * 4);
104     }
105 }
106 
107 static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
108                            ram_addr_t addr, int len)
109 {
110     int ret;
111 
112     ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
113 
114     if (s->depth == 24) {
115         ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
116                                        s->vram24_offset + addr * 4, len * 4);
117         ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
118                                        s->cplane_offset + addr * 4, len * 4);
119     }
120 
121     return ret;
122 }
123 
124 static void update_palette_entries(TCXState *s, int start, int end)
125 {
126     DisplaySurface *surface = qemu_console_surface(s->con);
127     int i;
128 
129     for (i = start; i < end; i++) {
130         if (is_surface_bgr(surface)) {
131             s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
132         } else {
133             s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
134         }
135     }
136     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
137 }
138 
139 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
140                             const uint8_t *s, int width)
141 {
142     int x;
143     uint8_t val;
144     uint32_t *p = (uint32_t *)d;
145 
146     for (x = 0; x < width; x++) {
147         val = *s++;
148         *p++ = s1->palette[val];
149     }
150 }
151 
152 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
153                               int y, int width)
154 {
155     int x, len;
156     uint32_t mask, bits;
157     uint32_t *p = (uint32_t *)d;
158 
159     y = y - s1->cursy;
160     mask = s1->cursmask[y];
161     bits = s1->cursbits[y];
162     len = MIN(width - s1->cursx, 32);
163     p = &p[s1->cursx];
164     for (x = 0; x < len; x++) {
165         if (mask & 0x80000000) {
166             if (bits & 0x80000000) {
167                 *p = s1->palette[259];
168             } else {
169                 *p = s1->palette[258];
170             }
171         }
172         p++;
173         mask <<= 1;
174         bits <<= 1;
175     }
176 }
177 
178 /*
179   XXX Could be much more optimal:
180   * detect if line/page/whole screen is in 24 bit mode
181   * if destination is also BGR, use memcpy
182   */
183 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
184                                      const uint8_t *s, int width,
185                                      const uint32_t *cplane,
186                                      const uint32_t *s24)
187 {
188     DisplaySurface *surface = qemu_console_surface(s1->con);
189     int x, bgr, r, g, b;
190     uint8_t val, *p8;
191     uint32_t *p = (uint32_t *)d;
192     uint32_t dval;
193     bgr = is_surface_bgr(surface);
194     for(x = 0; x < width; x++, s++, s24++) {
195         if (be32_to_cpu(*cplane) & 0x03000000) {
196             /* 24-bit direct, BGR order */
197             p8 = (uint8_t *)s24;
198             p8++;
199             b = *p8++;
200             g = *p8++;
201             r = *p8;
202             if (bgr)
203                 dval = rgb_to_pixel32bgr(r, g, b);
204             else
205                 dval = rgb_to_pixel32(r, g, b);
206         } else {
207             /* 8-bit pseudocolor */
208             val = *s;
209             dval = s1->palette[val];
210         }
211         *p++ = dval;
212         cplane++;
213     }
214 }
215 
216 /* Fixed line length 1024 allows us to do nice tricks not possible on
217    VGA... */
218 
219 static void tcx_update_display(void *opaque)
220 {
221     TCXState *ts = opaque;
222     DisplaySurface *surface = qemu_console_surface(ts->con);
223     ram_addr_t page;
224     DirtyBitmapSnapshot *snap = NULL;
225     int y, y_start, dd, ds;
226     uint8_t *d, *s;
227 
228     if (surface_bits_per_pixel(surface) != 32) {
229         return;
230     }
231 
232     page = 0;
233     y_start = -1;
234     d = surface_data(surface);
235     s = ts->vram;
236     dd = surface_stride(surface);
237     ds = 1024;
238 
239     memory_region_sync_dirty_bitmap(&ts->vram_mem);
240     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
241                                              memory_region_size(&ts->vram_mem),
242                                              DIRTY_MEMORY_VGA);
243 
244     for (y = 0; y < ts->height; y++, page += ds) {
245         if (tcx_check_dirty(ts, snap, page, ds)) {
246             if (y_start < 0)
247                 y_start = y;
248 
249             tcx_draw_line32(ts, d, s, ts->width);
250             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
251                 tcx_draw_cursor32(ts, d, y, ts->width);
252             }
253         } else {
254             if (y_start >= 0) {
255                 /* flush to display */
256                 dpy_gfx_update(ts->con, 0, y_start,
257                                ts->width, y - y_start);
258                 y_start = -1;
259             }
260         }
261         s += ds;
262         d += dd;
263     }
264     if (y_start >= 0) {
265         /* flush to display */
266         dpy_gfx_update(ts->con, 0, y_start,
267                        ts->width, y - y_start);
268     }
269     g_free(snap);
270 }
271 
272 static void tcx24_update_display(void *opaque)
273 {
274     TCXState *ts = opaque;
275     DisplaySurface *surface = qemu_console_surface(ts->con);
276     ram_addr_t page;
277     DirtyBitmapSnapshot *snap = NULL;
278     int y, y_start, dd, ds;
279     uint8_t *d, *s;
280     uint32_t *cptr, *s24;
281 
282     if (surface_bits_per_pixel(surface) != 32) {
283             return;
284     }
285 
286     page = 0;
287     y_start = -1;
288     d = surface_data(surface);
289     s = ts->vram;
290     s24 = ts->vram24;
291     cptr = ts->cplane;
292     dd = surface_stride(surface);
293     ds = 1024;
294 
295     memory_region_sync_dirty_bitmap(&ts->vram_mem);
296     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
297                                              memory_region_size(&ts->vram_mem),
298                                              DIRTY_MEMORY_VGA);
299 
300     for (y = 0; y < ts->height; y++, page += ds) {
301         if (tcx_check_dirty(ts, snap, page, ds)) {
302             if (y_start < 0)
303                 y_start = y;
304 
305             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
306             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
307                 tcx_draw_cursor32(ts, d, y, ts->width);
308             }
309         } else {
310             if (y_start >= 0) {
311                 /* flush to display */
312                 dpy_gfx_update(ts->con, 0, y_start,
313                                ts->width, y - y_start);
314                 y_start = -1;
315             }
316         }
317         d += dd;
318         s += ds;
319         cptr += ds;
320         s24 += ds;
321     }
322     if (y_start >= 0) {
323         /* flush to display */
324         dpy_gfx_update(ts->con, 0, y_start,
325                        ts->width, y - y_start);
326     }
327     g_free(snap);
328 }
329 
330 static void tcx_invalidate_display(void *opaque)
331 {
332     TCXState *s = opaque;
333 
334     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
335     qemu_console_resize(s->con, s->width, s->height);
336 }
337 
338 static void tcx24_invalidate_display(void *opaque)
339 {
340     TCXState *s = opaque;
341 
342     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
343     qemu_console_resize(s->con, s->width, s->height);
344 }
345 
346 static int vmstate_tcx_post_load(void *opaque, int version_id)
347 {
348     TCXState *s = opaque;
349 
350     update_palette_entries(s, 0, 256);
351     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
352     return 0;
353 }
354 
355 static const VMStateDescription vmstate_tcx = {
356     .name ="tcx",
357     .version_id = 4,
358     .minimum_version_id = 4,
359     .post_load = vmstate_tcx_post_load,
360     .fields = (VMStateField[]) {
361         VMSTATE_UINT16(height, TCXState),
362         VMSTATE_UINT16(width, TCXState),
363         VMSTATE_UINT16(depth, TCXState),
364         VMSTATE_BUFFER(r, TCXState),
365         VMSTATE_BUFFER(g, TCXState),
366         VMSTATE_BUFFER(b, TCXState),
367         VMSTATE_UINT8(dac_index, TCXState),
368         VMSTATE_UINT8(dac_state, TCXState),
369         VMSTATE_END_OF_LIST()
370     }
371 };
372 
373 static void tcx_reset(DeviceState *d)
374 {
375     TCXState *s = TCX(d);
376 
377     /* Initialize palette */
378     memset(s->r, 0, 260);
379     memset(s->g, 0, 260);
380     memset(s->b, 0, 260);
381     s->r[255] = s->g[255] = s->b[255] = 255;
382     s->r[256] = s->g[256] = s->b[256] = 255;
383     s->r[258] = s->g[258] = s->b[258] = 255;
384     update_palette_entries(s, 0, 260);
385     memset(s->vram, 0, MAXX*MAXY);
386     memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
387                               DIRTY_MEMORY_VGA);
388     s->dac_index = 0;
389     s->dac_state = 0;
390     s->cursx = 0xf000; /* Put cursor off screen */
391     s->cursy = 0xf000;
392 }
393 
394 static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
395                               unsigned size)
396 {
397     TCXState *s = opaque;
398     uint32_t val = 0;
399 
400     switch (s->dac_state) {
401     case 0:
402         val = s->r[s->dac_index] << 24;
403         s->dac_state++;
404         break;
405     case 1:
406         val = s->g[s->dac_index] << 24;
407         s->dac_state++;
408         break;
409     case 2:
410         val = s->b[s->dac_index] << 24;
411         s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
412     default:
413         s->dac_state = 0;
414         break;
415     }
416 
417     return val;
418 }
419 
420 static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
421                            unsigned size)
422 {
423     TCXState *s = opaque;
424     unsigned index;
425 
426     switch (addr) {
427     case 0: /* Address */
428         s->dac_index = val >> 24;
429         s->dac_state = 0;
430         break;
431     case 4:  /* Pixel colours */
432     case 12: /* Overlay (cursor) colours */
433         if (addr & 8) {
434             index = (s->dac_index & 3) + 256;
435         } else {
436             index = s->dac_index;
437         }
438         switch (s->dac_state) {
439         case 0:
440             s->r[index] = val >> 24;
441             update_palette_entries(s, index, index + 1);
442             s->dac_state++;
443             break;
444         case 1:
445             s->g[index] = val >> 24;
446             update_palette_entries(s, index, index + 1);
447             s->dac_state++;
448             break;
449         case 2:
450             s->b[index] = val >> 24;
451             update_palette_entries(s, index, index + 1);
452             s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
453         default:
454             s->dac_state = 0;
455             break;
456         }
457         break;
458     default: /* Control registers */
459         break;
460     }
461 }
462 
463 static const MemoryRegionOps tcx_dac_ops = {
464     .read = tcx_dac_readl,
465     .write = tcx_dac_writel,
466     .endianness = DEVICE_NATIVE_ENDIAN,
467     .valid = {
468         .min_access_size = 4,
469         .max_access_size = 4,
470     },
471 };
472 
473 static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
474                                unsigned size)
475 {
476     return 0;
477 }
478 
479 static void tcx_stip_writel(void *opaque, hwaddr addr,
480                             uint64_t val, unsigned size)
481 {
482     TCXState *s = opaque;
483     int i;
484     uint32_t col;
485 
486     if (!(addr & 4)) {
487         s->tmpblit = val;
488     } else {
489         addr = (addr >> 3) & 0xfffff;
490         col = cpu_to_be32(s->tmpblit);
491         if (s->depth == 24) {
492             for (i = 0; i < 32; i++)  {
493                 if (val & 0x80000000) {
494                     s->vram[addr + i] = s->tmpblit;
495                     s->vram24[addr + i] = col;
496                 }
497                 val <<= 1;
498             }
499         } else {
500             for (i = 0; i < 32; i++)  {
501                 if (val & 0x80000000) {
502                     s->vram[addr + i] = s->tmpblit;
503                 }
504                 val <<= 1;
505             }
506         }
507         tcx_set_dirty(s, addr, 32);
508     }
509 }
510 
511 static void tcx_rstip_writel(void *opaque, hwaddr addr,
512                              uint64_t val, unsigned size)
513 {
514     TCXState *s = opaque;
515     int i;
516     uint32_t col;
517 
518     if (!(addr & 4)) {
519         s->tmpblit = val;
520     } else {
521         addr = (addr >> 3) & 0xfffff;
522         col = cpu_to_be32(s->tmpblit);
523         if (s->depth == 24) {
524             for (i = 0; i < 32; i++) {
525                 if (val & 0x80000000) {
526                     s->vram[addr + i] = s->tmpblit;
527                     s->vram24[addr + i] = col;
528                     s->cplane[addr + i] = col;
529                 }
530                 val <<= 1;
531             }
532         } else {
533             for (i = 0; i < 32; i++)  {
534                 if (val & 0x80000000) {
535                     s->vram[addr + i] = s->tmpblit;
536                 }
537                 val <<= 1;
538             }
539         }
540         tcx_set_dirty(s, addr, 32);
541     }
542 }
543 
544 static const MemoryRegionOps tcx_stip_ops = {
545     .read = tcx_stip_readl,
546     .write = tcx_stip_writel,
547     .endianness = DEVICE_NATIVE_ENDIAN,
548     .valid = {
549         .min_access_size = 4,
550         .max_access_size = 4,
551     },
552 };
553 
554 static const MemoryRegionOps tcx_rstip_ops = {
555     .read = tcx_stip_readl,
556     .write = tcx_rstip_writel,
557     .endianness = DEVICE_NATIVE_ENDIAN,
558     .valid = {
559         .min_access_size = 4,
560         .max_access_size = 4,
561     },
562 };
563 
564 static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
565                                unsigned size)
566 {
567     return 0;
568 }
569 
570 static void tcx_blit_writel(void *opaque, hwaddr addr,
571                             uint64_t val, unsigned size)
572 {
573     TCXState *s = opaque;
574     uint32_t adsr, len;
575     int i;
576 
577     if (!(addr & 4)) {
578         s->tmpblit = val;
579     } else {
580         addr = (addr >> 3) & 0xfffff;
581         adsr = val & 0xffffff;
582         len = ((val >> 24) & 0x1f) + 1;
583         if (adsr == 0xffffff) {
584             memset(&s->vram[addr], s->tmpblit, len);
585             if (s->depth == 24) {
586                 val = s->tmpblit & 0xffffff;
587                 val = cpu_to_be32(val);
588                 for (i = 0; i < len; i++) {
589                     s->vram24[addr + i] = val;
590                 }
591             }
592         } else {
593             memcpy(&s->vram[addr], &s->vram[adsr], len);
594             if (s->depth == 24) {
595                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
596             }
597         }
598         tcx_set_dirty(s, addr, len);
599     }
600 }
601 
602 static void tcx_rblit_writel(void *opaque, hwaddr addr,
603                          uint64_t val, unsigned size)
604 {
605     TCXState *s = opaque;
606     uint32_t adsr, len;
607     int i;
608 
609     if (!(addr & 4)) {
610         s->tmpblit = val;
611     } else {
612         addr = (addr >> 3) & 0xfffff;
613         adsr = val & 0xffffff;
614         len = ((val >> 24) & 0x1f) + 1;
615         if (adsr == 0xffffff) {
616             memset(&s->vram[addr], s->tmpblit, len);
617             if (s->depth == 24) {
618                 val = s->tmpblit & 0xffffff;
619                 val = cpu_to_be32(val);
620                 for (i = 0; i < len; i++) {
621                     s->vram24[addr + i] = val;
622                     s->cplane[addr + i] = val;
623                 }
624             }
625         } else {
626             memcpy(&s->vram[addr], &s->vram[adsr], len);
627             if (s->depth == 24) {
628                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
629                 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
630             }
631         }
632         tcx_set_dirty(s, addr, len);
633     }
634 }
635 
636 static const MemoryRegionOps tcx_blit_ops = {
637     .read = tcx_blit_readl,
638     .write = tcx_blit_writel,
639     .endianness = DEVICE_NATIVE_ENDIAN,
640     .valid = {
641         .min_access_size = 4,
642         .max_access_size = 4,
643     },
644 };
645 
646 static const MemoryRegionOps tcx_rblit_ops = {
647     .read = tcx_blit_readl,
648     .write = tcx_rblit_writel,
649     .endianness = DEVICE_NATIVE_ENDIAN,
650     .valid = {
651         .min_access_size = 4,
652         .max_access_size = 4,
653     },
654 };
655 
656 static void tcx_invalidate_cursor_position(TCXState *s)
657 {
658     int ymin, ymax, start, end;
659 
660     /* invalidate only near the cursor */
661     ymin = s->cursy;
662     if (ymin >= s->height) {
663         return;
664     }
665     ymax = MIN(s->height, ymin + 32);
666     start = ymin * 1024;
667     end   = ymax * 1024;
668 
669     tcx_set_dirty(s, start, end - start);
670 }
671 
672 static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
673                             unsigned size)
674 {
675     TCXState *s = opaque;
676     uint64_t val;
677 
678     if (addr == TCX_THC_MISC) {
679         val = s->thcmisc | 0x02000000;
680     } else {
681         val = 0;
682     }
683     return val;
684 }
685 
686 static void tcx_thc_writel(void *opaque, hwaddr addr,
687                          uint64_t val, unsigned size)
688 {
689     TCXState *s = opaque;
690 
691     if (addr == TCX_THC_CURSXY) {
692         tcx_invalidate_cursor_position(s);
693         s->cursx = val >> 16;
694         s->cursy = val;
695         tcx_invalidate_cursor_position(s);
696     } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
697         s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
698         tcx_invalidate_cursor_position(s);
699     } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
700         s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
701         tcx_invalidate_cursor_position(s);
702     } else if (addr == TCX_THC_MISC) {
703         s->thcmisc = val;
704     }
705 
706 }
707 
708 static const MemoryRegionOps tcx_thc_ops = {
709     .read = tcx_thc_readl,
710     .write = tcx_thc_writel,
711     .endianness = DEVICE_NATIVE_ENDIAN,
712     .valid = {
713         .min_access_size = 4,
714         .max_access_size = 4,
715     },
716 };
717 
718 static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
719                             unsigned size)
720 {
721     return 0;
722 }
723 
724 static void tcx_dummy_writel(void *opaque, hwaddr addr,
725                          uint64_t val, unsigned size)
726 {
727     return;
728 }
729 
730 static const MemoryRegionOps tcx_dummy_ops = {
731     .read = tcx_dummy_readl,
732     .write = tcx_dummy_writel,
733     .endianness = DEVICE_NATIVE_ENDIAN,
734     .valid = {
735         .min_access_size = 4,
736         .max_access_size = 4,
737     },
738 };
739 
740 static const GraphicHwOps tcx_ops = {
741     .invalidate = tcx_invalidate_display,
742     .gfx_update = tcx_update_display,
743 };
744 
745 static const GraphicHwOps tcx24_ops = {
746     .invalidate = tcx24_invalidate_display,
747     .gfx_update = tcx24_update_display,
748 };
749 
750 static void tcx_initfn(Object *obj)
751 {
752     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
753     TCXState *s = TCX(obj);
754 
755     memory_region_init_ram_nomigrate(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
756                            &error_fatal);
757     memory_region_set_readonly(&s->rom, true);
758     sysbus_init_mmio(sbd, &s->rom);
759 
760     /* 2/STIP : Stippler */
761     memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
762                           TCX_STIP_NREGS);
763     sysbus_init_mmio(sbd, &s->stip);
764 
765     /* 3/BLIT : Blitter */
766     memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
767                           TCX_BLIT_NREGS);
768     sysbus_init_mmio(sbd, &s->blit);
769 
770     /* 5/RSTIP : Raw Stippler */
771     memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
772                           TCX_RSTIP_NREGS);
773     sysbus_init_mmio(sbd, &s->rstip);
774 
775     /* 6/RBLIT : Raw Blitter */
776     memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
777                           TCX_RBLIT_NREGS);
778     sysbus_init_mmio(sbd, &s->rblit);
779 
780     /* 7/TEC : ??? */
781     memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
782                           TCX_TEC_NREGS);
783     sysbus_init_mmio(sbd, &s->tec);
784 
785     /* 8/CMAP : DAC */
786     memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
787                           TCX_DAC_NREGS);
788     sysbus_init_mmio(sbd, &s->dac);
789 
790     /* 9/THC : Cursor */
791     memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
792                           TCX_THC_NREGS);
793     sysbus_init_mmio(sbd, &s->thc);
794 
795     /* 11/DHC : ??? */
796     memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
797                           TCX_DHC_NREGS);
798     sysbus_init_mmio(sbd, &s->dhc);
799 
800     /* 12/ALT : ??? */
801     memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
802                           TCX_ALT_NREGS);
803     sysbus_init_mmio(sbd, &s->alt);
804 }
805 
806 static void tcx_realizefn(DeviceState *dev, Error **errp)
807 {
808     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
809     TCXState *s = TCX(dev);
810     ram_addr_t vram_offset = 0;
811     int size, ret;
812     uint8_t *vram_base;
813     char *fcode_filename;
814 
815     memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram",
816                            s->vram_size * (1 + 4 + 4), &error_fatal);
817     vmstate_register_ram_global(&s->vram_mem);
818     memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
819     vram_base = memory_region_get_ram_ptr(&s->vram_mem);
820 
821     /* 10/ROM : FCode ROM */
822     vmstate_register_ram_global(&s->rom);
823     fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
824     if (fcode_filename) {
825         ret = load_image_mr(fcode_filename, &s->rom);
826         g_free(fcode_filename);
827         if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
828             error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
829         }
830     }
831 
832     /* 0/DFB8 : 8-bit plane */
833     s->vram = vram_base;
834     size = s->vram_size;
835     memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
836                              &s->vram_mem, vram_offset, size);
837     sysbus_init_mmio(sbd, &s->vram_8bit);
838     vram_offset += size;
839     vram_base += size;
840 
841     /* 1/DFB24 : 24bit plane */
842     size = s->vram_size * 4;
843     s->vram24 = (uint32_t *)vram_base;
844     s->vram24_offset = vram_offset;
845     memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
846                              &s->vram_mem, vram_offset, size);
847     sysbus_init_mmio(sbd, &s->vram_24bit);
848     vram_offset += size;
849     vram_base += size;
850 
851     /* 4/RDFB32 : Raw Framebuffer */
852     size = s->vram_size * 4;
853     s->cplane = (uint32_t *)vram_base;
854     s->cplane_offset = vram_offset;
855     memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
856                              &s->vram_mem, vram_offset, size);
857     sysbus_init_mmio(sbd, &s->vram_cplane);
858 
859     /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
860     if (s->depth == 8) {
861         memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
862                               "tcx.thc24", TCX_THC_NREGS);
863         sysbus_init_mmio(sbd, &s->thc24);
864     }
865 
866     sysbus_init_irq(sbd, &s->irq);
867 
868     if (s->depth == 8) {
869         s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s);
870     } else {
871         s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s);
872     }
873     s->thcmisc = 0;
874 
875     qemu_console_resize(s->con, s->width, s->height);
876 }
877 
878 static Property tcx_properties[] = {
879     DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
880     DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
881     DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
882     DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
883     DEFINE_PROP_END_OF_LIST(),
884 };
885 
886 static void tcx_class_init(ObjectClass *klass, void *data)
887 {
888     DeviceClass *dc = DEVICE_CLASS(klass);
889 
890     dc->realize = tcx_realizefn;
891     dc->reset = tcx_reset;
892     dc->vmsd = &vmstate_tcx;
893     dc->props = tcx_properties;
894 }
895 
896 static const TypeInfo tcx_info = {
897     .name          = TYPE_TCX,
898     .parent        = TYPE_SYS_BUS_DEVICE,
899     .instance_size = sizeof(TCXState),
900     .instance_init = tcx_initfn,
901     .class_init    = tcx_class_init,
902 };
903 
904 static void tcx_register_types(void)
905 {
906     type_register_static(&tcx_info);
907 }
908 
909 type_init(tcx_register_types)
910