xref: /qemu/hw/display/tcx.c (revision d6454270)
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 "qemu-common.h"
27 #include "qapi/error.h"
28 #include "ui/console.h"
29 #include "ui/pixel_ops.h"
30 #include "hw/loader.h"
31 #include "hw/sysbus.h"
32 #include "migration/vmstate.h"
33 #include "qemu/error-report.h"
34 #include "qemu/module.h"
35 
36 #define TCX_ROM_FILE "QEMU,tcx.bin"
37 #define FCODE_MAX_ROM_SIZE 0x10000
38 
39 #define MAXX 1024
40 #define MAXY 768
41 #define TCX_DAC_NREGS    16
42 #define TCX_THC_NREGS    0x1000
43 #define TCX_DHC_NREGS    0x4000
44 #define TCX_TEC_NREGS    0x1000
45 #define TCX_ALT_NREGS    0x8000
46 #define TCX_STIP_NREGS   0x800000
47 #define TCX_BLIT_NREGS   0x800000
48 #define TCX_RSTIP_NREGS  0x800000
49 #define TCX_RBLIT_NREGS  0x800000
50 
51 #define TCX_THC_MISC     0x818
52 #define TCX_THC_CURSXY   0x8fc
53 #define TCX_THC_CURSMASK 0x900
54 #define TCX_THC_CURSBITS 0x980
55 
56 #define TYPE_TCX "SUNW,tcx"
57 #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)
58 
59 typedef struct TCXState {
60     SysBusDevice parent_obj;
61 
62     QemuConsole *con;
63     qemu_irq irq;
64     uint8_t *vram;
65     uint32_t *vram24, *cplane;
66     hwaddr prom_addr;
67     MemoryRegion rom;
68     MemoryRegion vram_mem;
69     MemoryRegion vram_8bit;
70     MemoryRegion vram_24bit;
71     MemoryRegion stip;
72     MemoryRegion blit;
73     MemoryRegion vram_cplane;
74     MemoryRegion rstip;
75     MemoryRegion rblit;
76     MemoryRegion tec;
77     MemoryRegion dac;
78     MemoryRegion thc;
79     MemoryRegion dhc;
80     MemoryRegion alt;
81     MemoryRegion thc24;
82 
83     ram_addr_t vram24_offset, cplane_offset;
84     uint32_t tmpblit;
85     uint32_t vram_size;
86     uint32_t palette[260];
87     uint8_t r[260], g[260], b[260];
88     uint16_t width, height, depth;
89     uint8_t dac_index, dac_state;
90     uint32_t thcmisc;
91     uint32_t cursmask[32];
92     uint32_t cursbits[32];
93     uint16_t cursx;
94     uint16_t cursy;
95 } TCXState;
96 
97 static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
98 {
99     memory_region_set_dirty(&s->vram_mem, addr, len);
100 
101     if (s->depth == 24) {
102         memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
103                                 len * 4);
104         memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
105                                 len * 4);
106     }
107 }
108 
109 static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
110                            ram_addr_t addr, int len)
111 {
112     int ret;
113 
114     ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
115 
116     if (s->depth == 24) {
117         ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
118                                        s->vram24_offset + addr * 4, len * 4);
119         ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
120                                        s->cplane_offset + addr * 4, len * 4);
121     }
122 
123     return ret;
124 }
125 
126 static void update_palette_entries(TCXState *s, int start, int end)
127 {
128     DisplaySurface *surface = qemu_console_surface(s->con);
129     int i;
130 
131     for (i = start; i < end; i++) {
132         if (is_surface_bgr(surface)) {
133             s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
134         } else {
135             s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
136         }
137     }
138     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
139 }
140 
141 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
142                             const uint8_t *s, int width)
143 {
144     int x;
145     uint8_t val;
146     uint32_t *p = (uint32_t *)d;
147 
148     for (x = 0; x < width; x++) {
149         val = *s++;
150         *p++ = s1->palette[val];
151     }
152 }
153 
154 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
155                               int y, int width)
156 {
157     int x, len;
158     uint32_t mask, bits;
159     uint32_t *p = (uint32_t *)d;
160 
161     y = y - s1->cursy;
162     mask = s1->cursmask[y];
163     bits = s1->cursbits[y];
164     len = MIN(width - s1->cursx, 32);
165     p = &p[s1->cursx];
166     for (x = 0; x < len; x++) {
167         if (mask & 0x80000000) {
168             if (bits & 0x80000000) {
169                 *p = s1->palette[259];
170             } else {
171                 *p = s1->palette[258];
172             }
173         }
174         p++;
175         mask <<= 1;
176         bits <<= 1;
177     }
178 }
179 
180 /*
181   XXX Could be much more optimal:
182   * detect if line/page/whole screen is in 24 bit mode
183   * if destination is also BGR, use memcpy
184   */
185 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
186                                      const uint8_t *s, int width,
187                                      const uint32_t *cplane,
188                                      const uint32_t *s24)
189 {
190     DisplaySurface *surface = qemu_console_surface(s1->con);
191     int x, bgr, r, g, b;
192     uint8_t val, *p8;
193     uint32_t *p = (uint32_t *)d;
194     uint32_t dval;
195     bgr = is_surface_bgr(surface);
196     for(x = 0; x < width; x++, s++, s24++) {
197         if (be32_to_cpu(*cplane) & 0x03000000) {
198             /* 24-bit direct, BGR order */
199             p8 = (uint8_t *)s24;
200             p8++;
201             b = *p8++;
202             g = *p8++;
203             r = *p8;
204             if (bgr)
205                 dval = rgb_to_pixel32bgr(r, g, b);
206             else
207                 dval = rgb_to_pixel32(r, g, b);
208         } else {
209             /* 8-bit pseudocolor */
210             val = *s;
211             dval = s1->palette[val];
212         }
213         *p++ = dval;
214         cplane++;
215     }
216 }
217 
218 /* Fixed line length 1024 allows us to do nice tricks not possible on
219    VGA... */
220 
221 static void tcx_update_display(void *opaque)
222 {
223     TCXState *ts = opaque;
224     DisplaySurface *surface = qemu_console_surface(ts->con);
225     ram_addr_t page;
226     DirtyBitmapSnapshot *snap = NULL;
227     int y, y_start, dd, ds;
228     uint8_t *d, *s;
229 
230     if (surface_bits_per_pixel(surface) != 32) {
231         return;
232     }
233 
234     page = 0;
235     y_start = -1;
236     d = surface_data(surface);
237     s = ts->vram;
238     dd = surface_stride(surface);
239     ds = 1024;
240 
241     snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
242                                              memory_region_size(&ts->vram_mem),
243                                              DIRTY_MEMORY_VGA);
244 
245     for (y = 0; y < ts->height; y++, page += ds) {
246         if (tcx_check_dirty(ts, snap, page, ds)) {
247             if (y_start < 0)
248                 y_start = y;
249 
250             tcx_draw_line32(ts, d, s, ts->width);
251             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
252                 tcx_draw_cursor32(ts, d, y, ts->width);
253             }
254         } else {
255             if (y_start >= 0) {
256                 /* flush to display */
257                 dpy_gfx_update(ts->con, 0, y_start,
258                                ts->width, y - y_start);
259                 y_start = -1;
260             }
261         }
262         s += ds;
263         d += dd;
264     }
265     if (y_start >= 0) {
266         /* flush to display */
267         dpy_gfx_update(ts->con, 0, y_start,
268                        ts->width, y - y_start);
269     }
270     g_free(snap);
271 }
272 
273 static void tcx24_update_display(void *opaque)
274 {
275     TCXState *ts = opaque;
276     DisplaySurface *surface = qemu_console_surface(ts->con);
277     ram_addr_t page;
278     DirtyBitmapSnapshot *snap = NULL;
279     int y, y_start, dd, ds;
280     uint8_t *d, *s;
281     uint32_t *cptr, *s24;
282 
283     if (surface_bits_per_pixel(surface) != 32) {
284             return;
285     }
286 
287     page = 0;
288     y_start = -1;
289     d = surface_data(surface);
290     s = ts->vram;
291     s24 = ts->vram24;
292     cptr = ts->cplane;
293     dd = surface_stride(surface);
294     ds = 1024;
295 
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             warn_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