xref: /qemu/hw/display/tcx.c (revision d18e101225a83a5fa3a8dd9938a115ca2f06ca83)
1420557e8Sbellard /*
26f7e9aecSbellard  * QEMU TCX Frame buffer
3420557e8Sbellard  *
46f7e9aecSbellard  * Copyright (c) 2003-2005 Fabrice Bellard
5420557e8Sbellard  *
6420557e8Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
7420557e8Sbellard  * of this software and associated documentation files (the "Software"), to deal
8420557e8Sbellard  * in the Software without restriction, including without limitation the rights
9420557e8Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10420557e8Sbellard  * copies of the Software, and to permit persons to whom the Software is
11420557e8Sbellard  * furnished to do so, subject to the following conditions:
12420557e8Sbellard  *
13420557e8Sbellard  * The above copyright notice and this permission notice shall be included in
14420557e8Sbellard  * all copies or substantial portions of the Software.
15420557e8Sbellard  *
16420557e8Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17420557e8Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18420557e8Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19420557e8Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20420557e8Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21420557e8Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22420557e8Sbellard  * THE SOFTWARE.
23420557e8Sbellard  */
24f40070c3SBlue Swirl 
2547df5154SPeter Maydell #include "qemu/osdep.h"
26da34e65cSMarkus Armbruster #include "qapi/error.h"
27077805faSPaolo Bonzini #include "qemu-common.h"
2828ecbaeeSPaolo Bonzini #include "ui/console.h"
2928ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h"
30da87dd7bSMark Cave-Ayland #include "hw/loader.h"
3183c9f4caSPaolo Bonzini #include "hw/sysbus.h"
32d49b6836SMarkus Armbruster #include "qemu/error-report.h"
33420557e8Sbellard 
34da87dd7bSMark Cave-Ayland #define TCX_ROM_FILE "QEMU,tcx.bin"
35da87dd7bSMark Cave-Ayland #define FCODE_MAX_ROM_SIZE 0x10000
36da87dd7bSMark Cave-Ayland 
37420557e8Sbellard #define MAXX 1024
38420557e8Sbellard #define MAXY 768
396f7e9aecSbellard #define TCX_DAC_NREGS    16
4055d7bfe2SMark Cave-Ayland #define TCX_THC_NREGS    0x1000
4155d7bfe2SMark Cave-Ayland #define TCX_DHC_NREGS    0x4000
428508b89eSblueswir1 #define TCX_TEC_NREGS    0x1000
4355d7bfe2SMark Cave-Ayland #define TCX_ALT_NREGS    0x8000
4455d7bfe2SMark Cave-Ayland #define TCX_STIP_NREGS   0x800000
4555d7bfe2SMark Cave-Ayland #define TCX_BLIT_NREGS   0x800000
4655d7bfe2SMark Cave-Ayland #define TCX_RSTIP_NREGS  0x800000
4755d7bfe2SMark Cave-Ayland #define TCX_RBLIT_NREGS  0x800000
4855d7bfe2SMark Cave-Ayland 
4955d7bfe2SMark Cave-Ayland #define TCX_THC_MISC     0x818
5055d7bfe2SMark Cave-Ayland #define TCX_THC_CURSXY   0x8fc
5155d7bfe2SMark Cave-Ayland #define TCX_THC_CURSMASK 0x900
5255d7bfe2SMark Cave-Ayland #define TCX_THC_CURSBITS 0x980
53420557e8Sbellard 
5401774ddbSAndreas Färber #define TYPE_TCX "SUNW,tcx"
5501774ddbSAndreas Färber #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)
5601774ddbSAndreas Färber 
57420557e8Sbellard typedef struct TCXState {
5801774ddbSAndreas Färber     SysBusDevice parent_obj;
5901774ddbSAndreas Färber 
60c78f7137SGerd Hoffmann     QemuConsole *con;
6155d7bfe2SMark Cave-Ayland     qemu_irq irq;
628d5f07faSbellard     uint8_t *vram;
63eee0b836Sblueswir1     uint32_t *vram24, *cplane;
64da87dd7bSMark Cave-Ayland     hwaddr prom_addr;
65da87dd7bSMark Cave-Ayland     MemoryRegion rom;
66d08151bfSAvi Kivity     MemoryRegion vram_mem;
67d08151bfSAvi Kivity     MemoryRegion vram_8bit;
68d08151bfSAvi Kivity     MemoryRegion vram_24bit;
6955d7bfe2SMark Cave-Ayland     MemoryRegion stip;
7055d7bfe2SMark Cave-Ayland     MemoryRegion blit;
71d08151bfSAvi Kivity     MemoryRegion vram_cplane;
7255d7bfe2SMark Cave-Ayland     MemoryRegion rstip;
7355d7bfe2SMark Cave-Ayland     MemoryRegion rblit;
74d08151bfSAvi Kivity     MemoryRegion tec;
7555d7bfe2SMark Cave-Ayland     MemoryRegion dac;
7655d7bfe2SMark Cave-Ayland     MemoryRegion thc;
7755d7bfe2SMark Cave-Ayland     MemoryRegion dhc;
7855d7bfe2SMark Cave-Ayland     MemoryRegion alt;
79d08151bfSAvi Kivity     MemoryRegion thc24;
8055d7bfe2SMark Cave-Ayland 
81d08151bfSAvi Kivity     ram_addr_t vram24_offset, cplane_offset;
8255d7bfe2SMark Cave-Ayland     uint32_t tmpblit;
83ee6847d1SGerd Hoffmann     uint32_t vram_size;
8455d7bfe2SMark Cave-Ayland     uint32_t palette[260];
8555d7bfe2SMark Cave-Ayland     uint8_t r[260], g[260], b[260];
86427a66c3SBlue Swirl     uint16_t width, height, depth;
876f7e9aecSbellard     uint8_t dac_index, dac_state;
8855d7bfe2SMark Cave-Ayland     uint32_t thcmisc;
8955d7bfe2SMark Cave-Ayland     uint32_t cursmask[32];
9055d7bfe2SMark Cave-Ayland     uint32_t cursbits[32];
9155d7bfe2SMark Cave-Ayland     uint16_t cursx;
9255d7bfe2SMark Cave-Ayland     uint16_t cursy;
93420557e8Sbellard } TCXState;
94420557e8Sbellard 
959800b3c2SMark Cave-Ayland static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
96d3ffcafeSBlue Swirl {
979800b3c2SMark Cave-Ayland     memory_region_set_dirty(&s->vram_mem, addr, len);
984b865c28SMark Cave-Ayland 
994b865c28SMark Cave-Ayland     if (s->depth == 24) {
1004b865c28SMark Cave-Ayland         memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
1014b865c28SMark Cave-Ayland                                 len * 4);
1024b865c28SMark Cave-Ayland         memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
1034b865c28SMark Cave-Ayland                                 len * 4);
1044b865c28SMark Cave-Ayland     }
105d3ffcafeSBlue Swirl }
106d3ffcafeSBlue Swirl 
107427ee02bSMark Cave-Ayland static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len)
108d3ffcafeSBlue Swirl {
10955d7bfe2SMark Cave-Ayland     int ret;
11055d7bfe2SMark Cave-Ayland 
111427ee02bSMark Cave-Ayland     ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
112427ee02bSMark Cave-Ayland 
113427ee02bSMark Cave-Ayland     if (s->depth == 24) {
114427ee02bSMark Cave-Ayland         ret |= memory_region_get_dirty(&s->vram_mem,
115427ee02bSMark Cave-Ayland                                        s->vram24_offset + addr * 4, len * 4,
11655d7bfe2SMark Cave-Ayland                                        DIRTY_MEMORY_VGA);
117427ee02bSMark Cave-Ayland         ret |= memory_region_get_dirty(&s->vram_mem,
118427ee02bSMark Cave-Ayland                                        s->cplane_offset + addr * 4, len * 4,
11955d7bfe2SMark Cave-Ayland                                        DIRTY_MEMORY_VGA);
120427ee02bSMark Cave-Ayland     }
121427ee02bSMark Cave-Ayland 
12255d7bfe2SMark Cave-Ayland     return ret;
12355d7bfe2SMark Cave-Ayland }
12455d7bfe2SMark Cave-Ayland 
12536180430SMark Cave-Ayland static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len)
12655d7bfe2SMark Cave-Ayland {
12736180430SMark Cave-Ayland     memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
12836180430SMark Cave-Ayland 
12936180430SMark Cave-Ayland     if (s->depth == 24) {
13036180430SMark Cave-Ayland         memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
13136180430SMark Cave-Ayland                                   len * 4, DIRTY_MEMORY_VGA);
13236180430SMark Cave-Ayland         memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
13336180430SMark Cave-Ayland                                   len * 4, DIRTY_MEMORY_VGA);
13436180430SMark Cave-Ayland     }
135d3ffcafeSBlue Swirl }
13695219897Spbrook 
13721206a10Sbellard static void update_palette_entries(TCXState *s, int start, int end)
13821206a10Sbellard {
139c78f7137SGerd Hoffmann     DisplaySurface *surface = qemu_console_surface(s->con);
14021206a10Sbellard     int i;
141c78f7137SGerd Hoffmann 
14221206a10Sbellard     for (i = start; i < end; i++) {
143c78f7137SGerd Hoffmann         switch (surface_bits_per_pixel(surface)) {
14421206a10Sbellard         default:
14521206a10Sbellard         case 8:
14621206a10Sbellard             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
14721206a10Sbellard             break;
14821206a10Sbellard         case 15:
14921206a10Sbellard             s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
15021206a10Sbellard             break;
15121206a10Sbellard         case 16:
15221206a10Sbellard             s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
15321206a10Sbellard             break;
15421206a10Sbellard         case 32:
155c78f7137SGerd Hoffmann             if (is_surface_bgr(surface)) {
1567b5d76daSaliguori                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
157c78f7137SGerd Hoffmann             } else {
15821206a10Sbellard                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
159c78f7137SGerd Hoffmann             }
16021206a10Sbellard             break;
16121206a10Sbellard         }
16221206a10Sbellard     }
1639800b3c2SMark Cave-Ayland     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
164d3ffcafeSBlue Swirl }
16521206a10Sbellard 
166e80cfcfcSbellard static void tcx_draw_line32(TCXState *s1, uint8_t *d,
167e80cfcfcSbellard                             const uint8_t *s, int width)
168420557e8Sbellard {
169e80cfcfcSbellard     int x;
170e80cfcfcSbellard     uint8_t val;
1718bdc2159Sths     uint32_t *p = (uint32_t *)d;
172e80cfcfcSbellard 
173e80cfcfcSbellard     for (x = 0; x < width; x++) {
174e80cfcfcSbellard         val = *s++;
1758bdc2159Sths         *p++ = s1->palette[val];
176e80cfcfcSbellard     }
177420557e8Sbellard }
178420557e8Sbellard 
17921206a10Sbellard static void tcx_draw_line16(TCXState *s1, uint8_t *d,
180e80cfcfcSbellard                             const uint8_t *s, int width)
181e80cfcfcSbellard {
182e80cfcfcSbellard     int x;
183e80cfcfcSbellard     uint8_t val;
1848bdc2159Sths     uint16_t *p = (uint16_t *)d;
1858d5f07faSbellard 
186e80cfcfcSbellard     for (x = 0; x < width; x++) {
187e80cfcfcSbellard         val = *s++;
1888bdc2159Sths         *p++ = s1->palette[val];
189e80cfcfcSbellard     }
190e80cfcfcSbellard }
191e80cfcfcSbellard 
192e80cfcfcSbellard static void tcx_draw_line8(TCXState *s1, uint8_t *d,
193e80cfcfcSbellard                            const uint8_t *s, int width)
194e80cfcfcSbellard {
195e80cfcfcSbellard     int x;
196e80cfcfcSbellard     uint8_t val;
197e80cfcfcSbellard 
198e80cfcfcSbellard     for(x = 0; x < width; x++) {
199e80cfcfcSbellard         val = *s++;
20021206a10Sbellard         *d++ = s1->palette[val];
201e80cfcfcSbellard     }
202e80cfcfcSbellard }
203e80cfcfcSbellard 
20455d7bfe2SMark Cave-Ayland static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
20555d7bfe2SMark Cave-Ayland                               int y, int width)
20655d7bfe2SMark Cave-Ayland {
20755d7bfe2SMark Cave-Ayland     int x, len;
20855d7bfe2SMark Cave-Ayland     uint32_t mask, bits;
20955d7bfe2SMark Cave-Ayland     uint32_t *p = (uint32_t *)d;
21055d7bfe2SMark Cave-Ayland 
21155d7bfe2SMark Cave-Ayland     y = y - s1->cursy;
21255d7bfe2SMark Cave-Ayland     mask = s1->cursmask[y];
21355d7bfe2SMark Cave-Ayland     bits = s1->cursbits[y];
21455d7bfe2SMark Cave-Ayland     len = MIN(width - s1->cursx, 32);
21555d7bfe2SMark Cave-Ayland     p = &p[s1->cursx];
21655d7bfe2SMark Cave-Ayland     for (x = 0; x < len; x++) {
21755d7bfe2SMark Cave-Ayland         if (mask & 0x80000000) {
21855d7bfe2SMark Cave-Ayland             if (bits & 0x80000000) {
21955d7bfe2SMark Cave-Ayland                 *p = s1->palette[259];
22055d7bfe2SMark Cave-Ayland             } else {
22155d7bfe2SMark Cave-Ayland                 *p = s1->palette[258];
22255d7bfe2SMark Cave-Ayland             }
22355d7bfe2SMark Cave-Ayland         }
22455d7bfe2SMark Cave-Ayland         p++;
22555d7bfe2SMark Cave-Ayland         mask <<= 1;
22655d7bfe2SMark Cave-Ayland         bits <<= 1;
22755d7bfe2SMark Cave-Ayland     }
22855d7bfe2SMark Cave-Ayland }
22955d7bfe2SMark Cave-Ayland 
23055d7bfe2SMark Cave-Ayland static void tcx_draw_cursor16(TCXState *s1, uint8_t *d,
23155d7bfe2SMark Cave-Ayland                               int y, int width)
23255d7bfe2SMark Cave-Ayland {
23355d7bfe2SMark Cave-Ayland     int x, len;
23455d7bfe2SMark Cave-Ayland     uint32_t mask, bits;
23555d7bfe2SMark Cave-Ayland     uint16_t *p = (uint16_t *)d;
23655d7bfe2SMark Cave-Ayland 
23755d7bfe2SMark Cave-Ayland     y = y - s1->cursy;
23855d7bfe2SMark Cave-Ayland     mask = s1->cursmask[y];
23955d7bfe2SMark Cave-Ayland     bits = s1->cursbits[y];
24055d7bfe2SMark Cave-Ayland     len = MIN(width - s1->cursx, 32);
24155d7bfe2SMark Cave-Ayland     p = &p[s1->cursx];
24255d7bfe2SMark Cave-Ayland     for (x = 0; x < len; x++) {
24355d7bfe2SMark Cave-Ayland         if (mask & 0x80000000) {
24455d7bfe2SMark Cave-Ayland             if (bits & 0x80000000) {
24555d7bfe2SMark Cave-Ayland                 *p = s1->palette[259];
24655d7bfe2SMark Cave-Ayland             } else {
24755d7bfe2SMark Cave-Ayland                 *p = s1->palette[258];
24855d7bfe2SMark Cave-Ayland             }
24955d7bfe2SMark Cave-Ayland         }
25055d7bfe2SMark Cave-Ayland         p++;
25155d7bfe2SMark Cave-Ayland         mask <<= 1;
25255d7bfe2SMark Cave-Ayland         bits <<= 1;
25355d7bfe2SMark Cave-Ayland     }
25455d7bfe2SMark Cave-Ayland }
25555d7bfe2SMark Cave-Ayland 
25655d7bfe2SMark Cave-Ayland static void tcx_draw_cursor8(TCXState *s1, uint8_t *d,
25755d7bfe2SMark Cave-Ayland                               int y, int width)
25855d7bfe2SMark Cave-Ayland {
25955d7bfe2SMark Cave-Ayland     int x, len;
26055d7bfe2SMark Cave-Ayland     uint32_t mask, bits;
26155d7bfe2SMark Cave-Ayland 
26255d7bfe2SMark Cave-Ayland     y = y - s1->cursy;
26355d7bfe2SMark Cave-Ayland     mask = s1->cursmask[y];
26455d7bfe2SMark Cave-Ayland     bits = s1->cursbits[y];
26555d7bfe2SMark Cave-Ayland     len = MIN(width - s1->cursx, 32);
26655d7bfe2SMark Cave-Ayland     d = &d[s1->cursx];
26755d7bfe2SMark Cave-Ayland     for (x = 0; x < len; x++) {
26855d7bfe2SMark Cave-Ayland         if (mask & 0x80000000) {
26955d7bfe2SMark Cave-Ayland             if (bits & 0x80000000) {
27055d7bfe2SMark Cave-Ayland                 *d = s1->palette[259];
27155d7bfe2SMark Cave-Ayland             } else {
27255d7bfe2SMark Cave-Ayland                 *d = s1->palette[258];
27355d7bfe2SMark Cave-Ayland             }
27455d7bfe2SMark Cave-Ayland         }
27555d7bfe2SMark Cave-Ayland         d++;
27655d7bfe2SMark Cave-Ayland         mask <<= 1;
27755d7bfe2SMark Cave-Ayland         bits <<= 1;
27855d7bfe2SMark Cave-Ayland     }
27955d7bfe2SMark Cave-Ayland }
28055d7bfe2SMark Cave-Ayland 
281688ea2ebSblueswir1 /*
282688ea2ebSblueswir1   XXX Could be much more optimal:
283688ea2ebSblueswir1   * detect if line/page/whole screen is in 24 bit mode
284688ea2ebSblueswir1   * if destination is also BGR, use memcpy
285688ea2ebSblueswir1   */
286eee0b836Sblueswir1 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
287eee0b836Sblueswir1                                      const uint8_t *s, int width,
288eee0b836Sblueswir1                                      const uint32_t *cplane,
289eee0b836Sblueswir1                                      const uint32_t *s24)
290eee0b836Sblueswir1 {
291c78f7137SGerd Hoffmann     DisplaySurface *surface = qemu_console_surface(s1->con);
2927b5d76daSaliguori     int x, bgr, r, g, b;
293688ea2ebSblueswir1     uint8_t val, *p8;
294eee0b836Sblueswir1     uint32_t *p = (uint32_t *)d;
295eee0b836Sblueswir1     uint32_t dval;
296c78f7137SGerd Hoffmann     bgr = is_surface_bgr(surface);
297eee0b836Sblueswir1     for(x = 0; x < width; x++, s++, s24++) {
29855d7bfe2SMark Cave-Ayland         if (be32_to_cpu(*cplane) & 0x03000000) {
29955d7bfe2SMark Cave-Ayland             /* 24-bit direct, BGR order */
300688ea2ebSblueswir1             p8 = (uint8_t *)s24;
301688ea2ebSblueswir1             p8++;
302688ea2ebSblueswir1             b = *p8++;
303688ea2ebSblueswir1             g = *p8++;
304f7e683b8SBlue Swirl             r = *p8;
3057b5d76daSaliguori             if (bgr)
3067b5d76daSaliguori                 dval = rgb_to_pixel32bgr(r, g, b);
3077b5d76daSaliguori             else
308688ea2ebSblueswir1                 dval = rgb_to_pixel32(r, g, b);
309eee0b836Sblueswir1         } else {
31055d7bfe2SMark Cave-Ayland             /* 8-bit pseudocolor */
311eee0b836Sblueswir1             val = *s;
312eee0b836Sblueswir1             dval = s1->palette[val];
313eee0b836Sblueswir1         }
314eee0b836Sblueswir1         *p++ = dval;
31555d7bfe2SMark Cave-Ayland         cplane++;
316eee0b836Sblueswir1     }
317eee0b836Sblueswir1 }
318eee0b836Sblueswir1 
319e80cfcfcSbellard /* Fixed line length 1024 allows us to do nice tricks not possible on
320e80cfcfcSbellard    VGA... */
32155d7bfe2SMark Cave-Ayland 
32295219897Spbrook static void tcx_update_display(void *opaque)
323e80cfcfcSbellard {
324e80cfcfcSbellard     TCXState *ts = opaque;
325c78f7137SGerd Hoffmann     DisplaySurface *surface = qemu_console_surface(ts->con);
326c227f099SAnthony Liguori     ram_addr_t page, page_min, page_max;
327550be127Sbellard     int y, y_start, dd, ds;
328e80cfcfcSbellard     uint8_t *d, *s;
329b3ceef24Sblueswir1     void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
33055d7bfe2SMark Cave-Ayland     void (*fc)(TCXState *s1, uint8_t *dst, int y, int width);
331e80cfcfcSbellard 
332c78f7137SGerd Hoffmann     if (surface_bits_per_pixel(surface) == 0) {
333e80cfcfcSbellard         return;
334c78f7137SGerd Hoffmann     }
335c78f7137SGerd Hoffmann 
336d08151bfSAvi Kivity     page = 0;
337e80cfcfcSbellard     y_start = -1;
338c0c440f3SBlue Swirl     page_min = -1;
339550be127Sbellard     page_max = 0;
340c78f7137SGerd Hoffmann     d = surface_data(surface);
3416f7e9aecSbellard     s = ts->vram;
342c78f7137SGerd Hoffmann     dd = surface_stride(surface);
343e80cfcfcSbellard     ds = 1024;
344e80cfcfcSbellard 
345c78f7137SGerd Hoffmann     switch (surface_bits_per_pixel(surface)) {
346e80cfcfcSbellard     case 32:
347e80cfcfcSbellard         f = tcx_draw_line32;
34855d7bfe2SMark Cave-Ayland         fc = tcx_draw_cursor32;
349e80cfcfcSbellard         break;
35021206a10Sbellard     case 15:
35121206a10Sbellard     case 16:
35221206a10Sbellard         f = tcx_draw_line16;
35355d7bfe2SMark Cave-Ayland         fc = tcx_draw_cursor16;
354e80cfcfcSbellard         break;
355e80cfcfcSbellard     default:
356e80cfcfcSbellard     case 8:
357e80cfcfcSbellard         f = tcx_draw_line8;
35855d7bfe2SMark Cave-Ayland         fc = tcx_draw_cursor8;
359e80cfcfcSbellard         break;
360e80cfcfcSbellard     case 0:
361e80cfcfcSbellard         return;
362e80cfcfcSbellard     }
363e80cfcfcSbellard 
3645299c0f2SPaolo Bonzini     memory_region_sync_dirty_bitmap(&ts->vram_mem);
3650a97c6c4SMark Cave-Ayland     for (y = 0; y < ts->height; y++, page += ds) {
3660a97c6c4SMark Cave-Ayland         if (tcx_check_dirty(ts, page, ds)) {
367e80cfcfcSbellard             if (y_start < 0)
368e80cfcfcSbellard                 y_start = y;
369e80cfcfcSbellard             if (page < page_min)
370e80cfcfcSbellard                 page_min = page;
371e80cfcfcSbellard             if (page > page_max)
372e80cfcfcSbellard                 page_max = page;
37355d7bfe2SMark Cave-Ayland 
3746f7e9aecSbellard             f(ts, d, s, ts->width);
37555d7bfe2SMark Cave-Ayland             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
37655d7bfe2SMark Cave-Ayland                 fc(ts, d, y, ts->width);
37755d7bfe2SMark Cave-Ayland             }
378e80cfcfcSbellard         } else {
379e80cfcfcSbellard             if (y_start >= 0) {
380e80cfcfcSbellard                 /* flush to display */
381c78f7137SGerd Hoffmann                 dpy_gfx_update(ts->con, 0, y_start,
3826f7e9aecSbellard                                ts->width, y - y_start);
383e80cfcfcSbellard                 y_start = -1;
384e80cfcfcSbellard             }
385e80cfcfcSbellard         }
3860a97c6c4SMark Cave-Ayland         s += ds;
3870a97c6c4SMark Cave-Ayland         d += dd;
388e80cfcfcSbellard     }
389e80cfcfcSbellard     if (y_start >= 0) {
390e80cfcfcSbellard         /* flush to display */
391c78f7137SGerd Hoffmann         dpy_gfx_update(ts->con, 0, y_start,
3926f7e9aecSbellard                        ts->width, y - y_start);
393e80cfcfcSbellard     }
394e80cfcfcSbellard     /* reset modified pages */
395c0c440f3SBlue Swirl     if (page_max >= page_min) {
39636180430SMark Cave-Ayland         tcx_reset_dirty(ts, page_min, page_max - page_min);
397e80cfcfcSbellard     }
398e80cfcfcSbellard }
399e80cfcfcSbellard 
400eee0b836Sblueswir1 static void tcx24_update_display(void *opaque)
401eee0b836Sblueswir1 {
402eee0b836Sblueswir1     TCXState *ts = opaque;
403c78f7137SGerd Hoffmann     DisplaySurface *surface = qemu_console_surface(ts->con);
40466dcabeaSMark Cave-Ayland     ram_addr_t page, page_min, page_max;
405eee0b836Sblueswir1     int y, y_start, dd, ds;
406eee0b836Sblueswir1     uint8_t *d, *s;
407eee0b836Sblueswir1     uint32_t *cptr, *s24;
408eee0b836Sblueswir1 
409c78f7137SGerd Hoffmann     if (surface_bits_per_pixel(surface) != 32) {
410eee0b836Sblueswir1             return;
411c78f7137SGerd Hoffmann     }
412c78f7137SGerd Hoffmann 
413d08151bfSAvi Kivity     page = 0;
414eee0b836Sblueswir1     y_start = -1;
415c0c440f3SBlue Swirl     page_min = -1;
416eee0b836Sblueswir1     page_max = 0;
417c78f7137SGerd Hoffmann     d = surface_data(surface);
418eee0b836Sblueswir1     s = ts->vram;
419eee0b836Sblueswir1     s24 = ts->vram24;
420eee0b836Sblueswir1     cptr = ts->cplane;
421c78f7137SGerd Hoffmann     dd = surface_stride(surface);
422eee0b836Sblueswir1     ds = 1024;
423eee0b836Sblueswir1 
4245299c0f2SPaolo Bonzini     memory_region_sync_dirty_bitmap(&ts->vram_mem);
425*d18e1012SMark Cave-Ayland     for (y = 0; y < ts->height; y++, page += ds) {
426*d18e1012SMark Cave-Ayland         if (tcx_check_dirty(ts, page, ds)) {
427eee0b836Sblueswir1             if (y_start < 0)
428eee0b836Sblueswir1                 y_start = y;
429eee0b836Sblueswir1             if (page < page_min)
430eee0b836Sblueswir1                 page_min = page;
431eee0b836Sblueswir1             if (page > page_max)
432eee0b836Sblueswir1                 page_max = page;
433eee0b836Sblueswir1             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
43455d7bfe2SMark Cave-Ayland             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
43555d7bfe2SMark Cave-Ayland                 tcx_draw_cursor32(ts, d, y, ts->width);
43655d7bfe2SMark Cave-Ayland             }
437eee0b836Sblueswir1         } else {
438eee0b836Sblueswir1             if (y_start >= 0) {
439eee0b836Sblueswir1                 /* flush to display */
440c78f7137SGerd Hoffmann                 dpy_gfx_update(ts->con, 0, y_start,
441eee0b836Sblueswir1                                ts->width, y - y_start);
442eee0b836Sblueswir1                 y_start = -1;
443eee0b836Sblueswir1             }
444eee0b836Sblueswir1         }
445*d18e1012SMark Cave-Ayland         d += dd;
446*d18e1012SMark Cave-Ayland         s += ds;
447*d18e1012SMark Cave-Ayland         cptr += ds;
448*d18e1012SMark Cave-Ayland         s24 += ds;
449eee0b836Sblueswir1     }
450eee0b836Sblueswir1     if (y_start >= 0) {
451eee0b836Sblueswir1         /* flush to display */
452c78f7137SGerd Hoffmann         dpy_gfx_update(ts->con, 0, y_start,
453eee0b836Sblueswir1                        ts->width, y - y_start);
454eee0b836Sblueswir1     }
455eee0b836Sblueswir1     /* reset modified pages */
456c0c440f3SBlue Swirl     if (page_max >= page_min) {
45736180430SMark Cave-Ayland         tcx_reset_dirty(ts, page_min, page_max - page_min);
458eee0b836Sblueswir1     }
459eee0b836Sblueswir1 }
460eee0b836Sblueswir1 
46195219897Spbrook static void tcx_invalidate_display(void *opaque)
462420557e8Sbellard {
463420557e8Sbellard     TCXState *s = opaque;
464420557e8Sbellard 
4659800b3c2SMark Cave-Ayland     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
466c78f7137SGerd Hoffmann     qemu_console_resize(s->con, s->width, s->height);
467e80cfcfcSbellard }
468e80cfcfcSbellard 
469eee0b836Sblueswir1 static void tcx24_invalidate_display(void *opaque)
470eee0b836Sblueswir1 {
471eee0b836Sblueswir1     TCXState *s = opaque;
472eee0b836Sblueswir1 
4739800b3c2SMark Cave-Ayland     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
474c78f7137SGerd Hoffmann     qemu_console_resize(s->con, s->width, s->height);
475eee0b836Sblueswir1 }
476eee0b836Sblueswir1 
477e59fb374SJuan Quintela static int vmstate_tcx_post_load(void *opaque, int version_id)
478e80cfcfcSbellard {
479e80cfcfcSbellard     TCXState *s = opaque;
480e80cfcfcSbellard 
48121206a10Sbellard     update_palette_entries(s, 0, 256);
4829800b3c2SMark Cave-Ayland     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
483420557e8Sbellard     return 0;
484420557e8Sbellard }
485420557e8Sbellard 
486c0c41a4bSBlue Swirl static const VMStateDescription vmstate_tcx = {
487c0c41a4bSBlue Swirl     .name ="tcx",
488c0c41a4bSBlue Swirl     .version_id = 4,
489c0c41a4bSBlue Swirl     .minimum_version_id = 4,
490752ff2faSJuan Quintela     .post_load = vmstate_tcx_post_load,
491c0c41a4bSBlue Swirl     .fields = (VMStateField[]) {
492c0c41a4bSBlue Swirl         VMSTATE_UINT16(height, TCXState),
493c0c41a4bSBlue Swirl         VMSTATE_UINT16(width, TCXState),
494c0c41a4bSBlue Swirl         VMSTATE_UINT16(depth, TCXState),
495c0c41a4bSBlue Swirl         VMSTATE_BUFFER(r, TCXState),
496c0c41a4bSBlue Swirl         VMSTATE_BUFFER(g, TCXState),
497c0c41a4bSBlue Swirl         VMSTATE_BUFFER(b, TCXState),
498c0c41a4bSBlue Swirl         VMSTATE_UINT8(dac_index, TCXState),
499c0c41a4bSBlue Swirl         VMSTATE_UINT8(dac_state, TCXState),
500c0c41a4bSBlue Swirl         VMSTATE_END_OF_LIST()
501c0c41a4bSBlue Swirl     }
502c0c41a4bSBlue Swirl };
503c0c41a4bSBlue Swirl 
5047f23f812SMichael S. Tsirkin static void tcx_reset(DeviceState *d)
505420557e8Sbellard {
50601774ddbSAndreas Färber     TCXState *s = TCX(d);
507420557e8Sbellard 
508e80cfcfcSbellard     /* Initialize palette */
50955d7bfe2SMark Cave-Ayland     memset(s->r, 0, 260);
51055d7bfe2SMark Cave-Ayland     memset(s->g, 0, 260);
51155d7bfe2SMark Cave-Ayland     memset(s->b, 0, 260);
512e80cfcfcSbellard     s->r[255] = s->g[255] = s->b[255] = 255;
51355d7bfe2SMark Cave-Ayland     s->r[256] = s->g[256] = s->b[256] = 255;
51455d7bfe2SMark Cave-Ayland     s->r[258] = s->g[258] = s->b[258] = 255;
51555d7bfe2SMark Cave-Ayland     update_palette_entries(s, 0, 260);
516e80cfcfcSbellard     memset(s->vram, 0, MAXX*MAXY);
517d08151bfSAvi Kivity     memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
518d08151bfSAvi Kivity                               DIRTY_MEMORY_VGA);
5196f7e9aecSbellard     s->dac_index = 0;
5206f7e9aecSbellard     s->dac_state = 0;
52155d7bfe2SMark Cave-Ayland     s->cursx = 0xf000; /* Put cursor off screen */
52255d7bfe2SMark Cave-Ayland     s->cursy = 0xf000;
523420557e8Sbellard }
524420557e8Sbellard 
525a8170e5eSAvi Kivity static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
526d08151bfSAvi Kivity                               unsigned size)
5276f7e9aecSbellard {
52855d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
52955d7bfe2SMark Cave-Ayland     uint32_t val = 0;
53055d7bfe2SMark Cave-Ayland 
53155d7bfe2SMark Cave-Ayland     switch (s->dac_state) {
53255d7bfe2SMark Cave-Ayland     case 0:
53355d7bfe2SMark Cave-Ayland         val = s->r[s->dac_index] << 24;
53455d7bfe2SMark Cave-Ayland         s->dac_state++;
53555d7bfe2SMark Cave-Ayland         break;
53655d7bfe2SMark Cave-Ayland     case 1:
53755d7bfe2SMark Cave-Ayland         val = s->g[s->dac_index] << 24;
53855d7bfe2SMark Cave-Ayland         s->dac_state++;
53955d7bfe2SMark Cave-Ayland         break;
54055d7bfe2SMark Cave-Ayland     case 2:
54155d7bfe2SMark Cave-Ayland         val = s->b[s->dac_index] << 24;
54255d7bfe2SMark Cave-Ayland         s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
54355d7bfe2SMark Cave-Ayland     default:
54455d7bfe2SMark Cave-Ayland         s->dac_state = 0;
54555d7bfe2SMark Cave-Ayland         break;
54655d7bfe2SMark Cave-Ayland     }
54755d7bfe2SMark Cave-Ayland 
54855d7bfe2SMark Cave-Ayland     return val;
5496f7e9aecSbellard }
5506f7e9aecSbellard 
551a8170e5eSAvi Kivity static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
552d08151bfSAvi Kivity                            unsigned size)
5536f7e9aecSbellard {
5546f7e9aecSbellard     TCXState *s = opaque;
55555d7bfe2SMark Cave-Ayland     unsigned index;
5566f7e9aecSbellard 
557e64d7d59Sblueswir1     switch (addr) {
55855d7bfe2SMark Cave-Ayland     case 0: /* Address */
5596f7e9aecSbellard         s->dac_index = val >> 24;
5606f7e9aecSbellard         s->dac_state = 0;
5616f7e9aecSbellard         break;
56255d7bfe2SMark Cave-Ayland     case 4:  /* Pixel colours */
56355d7bfe2SMark Cave-Ayland     case 12: /* Overlay (cursor) colours */
56455d7bfe2SMark Cave-Ayland         if (addr & 8) {
56555d7bfe2SMark Cave-Ayland             index = (s->dac_index & 3) + 256;
56655d7bfe2SMark Cave-Ayland         } else {
56755d7bfe2SMark Cave-Ayland             index = s->dac_index;
56855d7bfe2SMark Cave-Ayland         }
5696f7e9aecSbellard         switch (s->dac_state) {
5706f7e9aecSbellard         case 0:
57155d7bfe2SMark Cave-Ayland             s->r[index] = val >> 24;
57255d7bfe2SMark Cave-Ayland             update_palette_entries(s, index, index + 1);
5736f7e9aecSbellard             s->dac_state++;
5746f7e9aecSbellard             break;
5756f7e9aecSbellard         case 1:
57655d7bfe2SMark Cave-Ayland             s->g[index] = val >> 24;
57755d7bfe2SMark Cave-Ayland             update_palette_entries(s, index, index + 1);
5786f7e9aecSbellard             s->dac_state++;
5796f7e9aecSbellard             break;
5806f7e9aecSbellard         case 2:
58155d7bfe2SMark Cave-Ayland             s->b[index] = val >> 24;
58255d7bfe2SMark Cave-Ayland             update_palette_entries(s, index, index + 1);
58355d7bfe2SMark Cave-Ayland             s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
5846f7e9aecSbellard         default:
5856f7e9aecSbellard             s->dac_state = 0;
5866f7e9aecSbellard             break;
5876f7e9aecSbellard         }
5886f7e9aecSbellard         break;
58955d7bfe2SMark Cave-Ayland     default: /* Control registers */
5906f7e9aecSbellard         break;
5916f7e9aecSbellard     }
5926f7e9aecSbellard }
5936f7e9aecSbellard 
594d08151bfSAvi Kivity static const MemoryRegionOps tcx_dac_ops = {
595d08151bfSAvi Kivity     .read = tcx_dac_readl,
596d08151bfSAvi Kivity     .write = tcx_dac_writel,
597d08151bfSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
598d08151bfSAvi Kivity     .valid = {
599d08151bfSAvi Kivity         .min_access_size = 4,
600d08151bfSAvi Kivity         .max_access_size = 4,
601d08151bfSAvi Kivity     },
6026f7e9aecSbellard };
6036f7e9aecSbellard 
60455d7bfe2SMark Cave-Ayland static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
605d08151bfSAvi Kivity                                unsigned size)
6068508b89eSblueswir1 {
6078508b89eSblueswir1     return 0;
6088508b89eSblueswir1 }
6098508b89eSblueswir1 
61055d7bfe2SMark Cave-Ayland static void tcx_stip_writel(void *opaque, hwaddr addr,
611d08151bfSAvi Kivity                             uint64_t val, unsigned size)
6128508b89eSblueswir1 {
61355d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
61455d7bfe2SMark Cave-Ayland     int i;
61555d7bfe2SMark Cave-Ayland     uint32_t col;
61655d7bfe2SMark Cave-Ayland 
61755d7bfe2SMark Cave-Ayland     if (!(addr & 4)) {
61855d7bfe2SMark Cave-Ayland         s->tmpblit = val;
61955d7bfe2SMark Cave-Ayland     } else {
62055d7bfe2SMark Cave-Ayland         addr = (addr >> 3) & 0xfffff;
62155d7bfe2SMark Cave-Ayland         col = cpu_to_be32(s->tmpblit);
62255d7bfe2SMark Cave-Ayland         if (s->depth == 24) {
62355d7bfe2SMark Cave-Ayland             for (i = 0; i < 32; i++)  {
62455d7bfe2SMark Cave-Ayland                 if (val & 0x80000000) {
62555d7bfe2SMark Cave-Ayland                     s->vram[addr + i] = s->tmpblit;
62655d7bfe2SMark Cave-Ayland                     s->vram24[addr + i] = col;
62755d7bfe2SMark Cave-Ayland                 }
62855d7bfe2SMark Cave-Ayland                 val <<= 1;
62955d7bfe2SMark Cave-Ayland             }
63055d7bfe2SMark Cave-Ayland         } else {
63155d7bfe2SMark Cave-Ayland             for (i = 0; i < 32; i++)  {
63255d7bfe2SMark Cave-Ayland                 if (val & 0x80000000) {
63355d7bfe2SMark Cave-Ayland                     s->vram[addr + i] = s->tmpblit;
63455d7bfe2SMark Cave-Ayland                 }
63555d7bfe2SMark Cave-Ayland                 val <<= 1;
63655d7bfe2SMark Cave-Ayland             }
63755d7bfe2SMark Cave-Ayland         }
63855d7bfe2SMark Cave-Ayland         memory_region_set_dirty(&s->vram_mem, addr, 32);
63955d7bfe2SMark Cave-Ayland     }
6408508b89eSblueswir1 }
6418508b89eSblueswir1 
64255d7bfe2SMark Cave-Ayland static void tcx_rstip_writel(void *opaque, hwaddr addr,
64355d7bfe2SMark Cave-Ayland                              uint64_t val, unsigned size)
64455d7bfe2SMark Cave-Ayland {
64555d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
64655d7bfe2SMark Cave-Ayland     int i;
64755d7bfe2SMark Cave-Ayland     uint32_t col;
64855d7bfe2SMark Cave-Ayland 
64955d7bfe2SMark Cave-Ayland     if (!(addr & 4)) {
65055d7bfe2SMark Cave-Ayland         s->tmpblit = val;
65155d7bfe2SMark Cave-Ayland     } else {
65255d7bfe2SMark Cave-Ayland         addr = (addr >> 3) & 0xfffff;
65355d7bfe2SMark Cave-Ayland         col = cpu_to_be32(s->tmpblit);
65455d7bfe2SMark Cave-Ayland         if (s->depth == 24) {
65555d7bfe2SMark Cave-Ayland             for (i = 0; i < 32; i++) {
65655d7bfe2SMark Cave-Ayland                 if (val & 0x80000000) {
65755d7bfe2SMark Cave-Ayland                     s->vram[addr + i] = s->tmpblit;
65855d7bfe2SMark Cave-Ayland                     s->vram24[addr + i] = col;
65955d7bfe2SMark Cave-Ayland                     s->cplane[addr + i] = col;
66055d7bfe2SMark Cave-Ayland                 }
66155d7bfe2SMark Cave-Ayland                 val <<= 1;
66255d7bfe2SMark Cave-Ayland             }
66355d7bfe2SMark Cave-Ayland         } else {
66455d7bfe2SMark Cave-Ayland             for (i = 0; i < 32; i++)  {
66555d7bfe2SMark Cave-Ayland                 if (val & 0x80000000) {
66655d7bfe2SMark Cave-Ayland                     s->vram[addr + i] = s->tmpblit;
66755d7bfe2SMark Cave-Ayland                 }
66855d7bfe2SMark Cave-Ayland                 val <<= 1;
66955d7bfe2SMark Cave-Ayland             }
67055d7bfe2SMark Cave-Ayland         }
67155d7bfe2SMark Cave-Ayland         memory_region_set_dirty(&s->vram_mem, addr, 32);
67255d7bfe2SMark Cave-Ayland     }
67355d7bfe2SMark Cave-Ayland }
67455d7bfe2SMark Cave-Ayland 
67555d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_stip_ops = {
67655d7bfe2SMark Cave-Ayland     .read = tcx_stip_readl,
67755d7bfe2SMark Cave-Ayland     .write = tcx_stip_writel,
67855d7bfe2SMark Cave-Ayland     .endianness = DEVICE_NATIVE_ENDIAN,
67955d7bfe2SMark Cave-Ayland     .valid = {
68055d7bfe2SMark Cave-Ayland         .min_access_size = 4,
68155d7bfe2SMark Cave-Ayland         .max_access_size = 4,
68255d7bfe2SMark Cave-Ayland     },
68355d7bfe2SMark Cave-Ayland };
68455d7bfe2SMark Cave-Ayland 
68555d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_rstip_ops = {
68655d7bfe2SMark Cave-Ayland     .read = tcx_stip_readl,
68755d7bfe2SMark Cave-Ayland     .write = tcx_rstip_writel,
68855d7bfe2SMark Cave-Ayland     .endianness = DEVICE_NATIVE_ENDIAN,
68955d7bfe2SMark Cave-Ayland     .valid = {
69055d7bfe2SMark Cave-Ayland         .min_access_size = 4,
69155d7bfe2SMark Cave-Ayland         .max_access_size = 4,
69255d7bfe2SMark Cave-Ayland     },
69355d7bfe2SMark Cave-Ayland };
69455d7bfe2SMark Cave-Ayland 
69555d7bfe2SMark Cave-Ayland static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
69655d7bfe2SMark Cave-Ayland                                unsigned size)
69755d7bfe2SMark Cave-Ayland {
69855d7bfe2SMark Cave-Ayland     return 0;
69955d7bfe2SMark Cave-Ayland }
70055d7bfe2SMark Cave-Ayland 
70155d7bfe2SMark Cave-Ayland static void tcx_blit_writel(void *opaque, hwaddr addr,
70255d7bfe2SMark Cave-Ayland                             uint64_t val, unsigned size)
70355d7bfe2SMark Cave-Ayland {
70455d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
70555d7bfe2SMark Cave-Ayland     uint32_t adsr, len;
70655d7bfe2SMark Cave-Ayland     int i;
70755d7bfe2SMark Cave-Ayland 
70855d7bfe2SMark Cave-Ayland     if (!(addr & 4)) {
70955d7bfe2SMark Cave-Ayland         s->tmpblit = val;
71055d7bfe2SMark Cave-Ayland     } else {
71155d7bfe2SMark Cave-Ayland         addr = (addr >> 3) & 0xfffff;
71255d7bfe2SMark Cave-Ayland         adsr = val & 0xffffff;
71355d7bfe2SMark Cave-Ayland         len = ((val >> 24) & 0x1f) + 1;
71455d7bfe2SMark Cave-Ayland         if (adsr == 0xffffff) {
71555d7bfe2SMark Cave-Ayland             memset(&s->vram[addr], s->tmpblit, len);
71655d7bfe2SMark Cave-Ayland             if (s->depth == 24) {
71755d7bfe2SMark Cave-Ayland                 val = s->tmpblit & 0xffffff;
71855d7bfe2SMark Cave-Ayland                 val = cpu_to_be32(val);
71955d7bfe2SMark Cave-Ayland                 for (i = 0; i < len; i++) {
72055d7bfe2SMark Cave-Ayland                     s->vram24[addr + i] = val;
72155d7bfe2SMark Cave-Ayland                 }
72255d7bfe2SMark Cave-Ayland             }
72355d7bfe2SMark Cave-Ayland         } else {
72455d7bfe2SMark Cave-Ayland             memcpy(&s->vram[addr], &s->vram[adsr], len);
72555d7bfe2SMark Cave-Ayland             if (s->depth == 24) {
72655d7bfe2SMark Cave-Ayland                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
72755d7bfe2SMark Cave-Ayland             }
72855d7bfe2SMark Cave-Ayland         }
72955d7bfe2SMark Cave-Ayland         memory_region_set_dirty(&s->vram_mem, addr, len);
73055d7bfe2SMark Cave-Ayland     }
73155d7bfe2SMark Cave-Ayland }
73255d7bfe2SMark Cave-Ayland 
73355d7bfe2SMark Cave-Ayland static void tcx_rblit_writel(void *opaque, hwaddr addr,
73455d7bfe2SMark Cave-Ayland                          uint64_t val, unsigned size)
73555d7bfe2SMark Cave-Ayland {
73655d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
73755d7bfe2SMark Cave-Ayland     uint32_t adsr, len;
73855d7bfe2SMark Cave-Ayland     int i;
73955d7bfe2SMark Cave-Ayland 
74055d7bfe2SMark Cave-Ayland     if (!(addr & 4)) {
74155d7bfe2SMark Cave-Ayland         s->tmpblit = val;
74255d7bfe2SMark Cave-Ayland     } else {
74355d7bfe2SMark Cave-Ayland         addr = (addr >> 3) & 0xfffff;
74455d7bfe2SMark Cave-Ayland         adsr = val & 0xffffff;
74555d7bfe2SMark Cave-Ayland         len = ((val >> 24) & 0x1f) + 1;
74655d7bfe2SMark Cave-Ayland         if (adsr == 0xffffff) {
74755d7bfe2SMark Cave-Ayland             memset(&s->vram[addr], s->tmpblit, len);
74855d7bfe2SMark Cave-Ayland             if (s->depth == 24) {
74955d7bfe2SMark Cave-Ayland                 val = s->tmpblit & 0xffffff;
75055d7bfe2SMark Cave-Ayland                 val = cpu_to_be32(val);
75155d7bfe2SMark Cave-Ayland                 for (i = 0; i < len; i++) {
75255d7bfe2SMark Cave-Ayland                     s->vram24[addr + i] = val;
75355d7bfe2SMark Cave-Ayland                     s->cplane[addr + i] = val;
75455d7bfe2SMark Cave-Ayland                 }
75555d7bfe2SMark Cave-Ayland             }
75655d7bfe2SMark Cave-Ayland         } else {
75755d7bfe2SMark Cave-Ayland             memcpy(&s->vram[addr], &s->vram[adsr], len);
75855d7bfe2SMark Cave-Ayland             if (s->depth == 24) {
75955d7bfe2SMark Cave-Ayland                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
76055d7bfe2SMark Cave-Ayland                 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
76155d7bfe2SMark Cave-Ayland             }
76255d7bfe2SMark Cave-Ayland         }
76355d7bfe2SMark Cave-Ayland         memory_region_set_dirty(&s->vram_mem, addr, len);
76455d7bfe2SMark Cave-Ayland     }
76555d7bfe2SMark Cave-Ayland }
76655d7bfe2SMark Cave-Ayland 
76755d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_blit_ops = {
76855d7bfe2SMark Cave-Ayland     .read = tcx_blit_readl,
76955d7bfe2SMark Cave-Ayland     .write = tcx_blit_writel,
77055d7bfe2SMark Cave-Ayland     .endianness = DEVICE_NATIVE_ENDIAN,
77155d7bfe2SMark Cave-Ayland     .valid = {
77255d7bfe2SMark Cave-Ayland         .min_access_size = 4,
77355d7bfe2SMark Cave-Ayland         .max_access_size = 4,
77455d7bfe2SMark Cave-Ayland     },
77555d7bfe2SMark Cave-Ayland };
77655d7bfe2SMark Cave-Ayland 
77755d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_rblit_ops = {
77855d7bfe2SMark Cave-Ayland     .read = tcx_blit_readl,
77955d7bfe2SMark Cave-Ayland     .write = tcx_rblit_writel,
78055d7bfe2SMark Cave-Ayland     .endianness = DEVICE_NATIVE_ENDIAN,
78155d7bfe2SMark Cave-Ayland     .valid = {
78255d7bfe2SMark Cave-Ayland         .min_access_size = 4,
78355d7bfe2SMark Cave-Ayland         .max_access_size = 4,
78455d7bfe2SMark Cave-Ayland     },
78555d7bfe2SMark Cave-Ayland };
78655d7bfe2SMark Cave-Ayland 
78755d7bfe2SMark Cave-Ayland static void tcx_invalidate_cursor_position(TCXState *s)
78855d7bfe2SMark Cave-Ayland {
78955d7bfe2SMark Cave-Ayland     int ymin, ymax, start, end;
79055d7bfe2SMark Cave-Ayland 
79155d7bfe2SMark Cave-Ayland     /* invalidate only near the cursor */
79255d7bfe2SMark Cave-Ayland     ymin = s->cursy;
79355d7bfe2SMark Cave-Ayland     if (ymin >= s->height) {
79455d7bfe2SMark Cave-Ayland         return;
79555d7bfe2SMark Cave-Ayland     }
79655d7bfe2SMark Cave-Ayland     ymax = MIN(s->height, ymin + 32);
79755d7bfe2SMark Cave-Ayland     start = ymin * 1024;
79855d7bfe2SMark Cave-Ayland     end   = ymax * 1024;
79955d7bfe2SMark Cave-Ayland 
80055d7bfe2SMark Cave-Ayland     memory_region_set_dirty(&s->vram_mem, start, end-start);
80155d7bfe2SMark Cave-Ayland }
80255d7bfe2SMark Cave-Ayland 
80355d7bfe2SMark Cave-Ayland static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
80455d7bfe2SMark Cave-Ayland                             unsigned size)
80555d7bfe2SMark Cave-Ayland {
80655d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
80755d7bfe2SMark Cave-Ayland     uint64_t val;
80855d7bfe2SMark Cave-Ayland 
80955d7bfe2SMark Cave-Ayland     if (addr == TCX_THC_MISC) {
81055d7bfe2SMark Cave-Ayland         val = s->thcmisc | 0x02000000;
81155d7bfe2SMark Cave-Ayland     } else {
81255d7bfe2SMark Cave-Ayland         val = 0;
81355d7bfe2SMark Cave-Ayland     }
81455d7bfe2SMark Cave-Ayland     return val;
81555d7bfe2SMark Cave-Ayland }
81655d7bfe2SMark Cave-Ayland 
81755d7bfe2SMark Cave-Ayland static void tcx_thc_writel(void *opaque, hwaddr addr,
81855d7bfe2SMark Cave-Ayland                          uint64_t val, unsigned size)
81955d7bfe2SMark Cave-Ayland {
82055d7bfe2SMark Cave-Ayland     TCXState *s = opaque;
82155d7bfe2SMark Cave-Ayland 
82255d7bfe2SMark Cave-Ayland     if (addr == TCX_THC_CURSXY) {
82355d7bfe2SMark Cave-Ayland         tcx_invalidate_cursor_position(s);
82455d7bfe2SMark Cave-Ayland         s->cursx = val >> 16;
82555d7bfe2SMark Cave-Ayland         s->cursy = val;
82655d7bfe2SMark Cave-Ayland         tcx_invalidate_cursor_position(s);
82755d7bfe2SMark Cave-Ayland     } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
82855d7bfe2SMark Cave-Ayland         s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
82955d7bfe2SMark Cave-Ayland         tcx_invalidate_cursor_position(s);
83055d7bfe2SMark Cave-Ayland     } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
83155d7bfe2SMark Cave-Ayland         s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
83255d7bfe2SMark Cave-Ayland         tcx_invalidate_cursor_position(s);
83355d7bfe2SMark Cave-Ayland     } else if (addr == TCX_THC_MISC) {
83455d7bfe2SMark Cave-Ayland         s->thcmisc = val;
83555d7bfe2SMark Cave-Ayland     }
83655d7bfe2SMark Cave-Ayland 
83755d7bfe2SMark Cave-Ayland }
83855d7bfe2SMark Cave-Ayland 
83955d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_thc_ops = {
84055d7bfe2SMark Cave-Ayland     .read = tcx_thc_readl,
84155d7bfe2SMark Cave-Ayland     .write = tcx_thc_writel,
84255d7bfe2SMark Cave-Ayland     .endianness = DEVICE_NATIVE_ENDIAN,
84355d7bfe2SMark Cave-Ayland     .valid = {
84455d7bfe2SMark Cave-Ayland         .min_access_size = 4,
84555d7bfe2SMark Cave-Ayland         .max_access_size = 4,
84655d7bfe2SMark Cave-Ayland     },
84755d7bfe2SMark Cave-Ayland };
84855d7bfe2SMark Cave-Ayland 
84955d7bfe2SMark Cave-Ayland static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
85055d7bfe2SMark Cave-Ayland                             unsigned size)
85155d7bfe2SMark Cave-Ayland {
85255d7bfe2SMark Cave-Ayland     return 0;
85355d7bfe2SMark Cave-Ayland }
85455d7bfe2SMark Cave-Ayland 
85555d7bfe2SMark Cave-Ayland static void tcx_dummy_writel(void *opaque, hwaddr addr,
85655d7bfe2SMark Cave-Ayland                          uint64_t val, unsigned size)
85755d7bfe2SMark Cave-Ayland {
85855d7bfe2SMark Cave-Ayland     return;
85955d7bfe2SMark Cave-Ayland }
86055d7bfe2SMark Cave-Ayland 
86155d7bfe2SMark Cave-Ayland static const MemoryRegionOps tcx_dummy_ops = {
86255d7bfe2SMark Cave-Ayland     .read = tcx_dummy_readl,
86355d7bfe2SMark Cave-Ayland     .write = tcx_dummy_writel,
864d08151bfSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
865d08151bfSAvi Kivity     .valid = {
866d08151bfSAvi Kivity         .min_access_size = 4,
867d08151bfSAvi Kivity         .max_access_size = 4,
868d08151bfSAvi Kivity     },
8698508b89eSblueswir1 };
8708508b89eSblueswir1 
871380cd056SGerd Hoffmann static const GraphicHwOps tcx_ops = {
872380cd056SGerd Hoffmann     .invalidate = tcx_invalidate_display,
873380cd056SGerd Hoffmann     .gfx_update = tcx_update_display,
874380cd056SGerd Hoffmann };
875380cd056SGerd Hoffmann 
876380cd056SGerd Hoffmann static const GraphicHwOps tcx24_ops = {
877380cd056SGerd Hoffmann     .invalidate = tcx24_invalidate_display,
878380cd056SGerd Hoffmann     .gfx_update = tcx24_update_display,
879380cd056SGerd Hoffmann };
880380cd056SGerd Hoffmann 
88101b91ac2SMark Cave-Ayland static void tcx_initfn(Object *obj)
88201b91ac2SMark Cave-Ayland {
88301b91ac2SMark Cave-Ayland     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
88401b91ac2SMark Cave-Ayland     TCXState *s = TCX(obj);
88501b91ac2SMark Cave-Ayland 
886b21de199SThomas Huth     memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
887f8ed85acSMarkus Armbruster                            &error_fatal);
88801b91ac2SMark Cave-Ayland     memory_region_set_readonly(&s->rom, true);
88901b91ac2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->rom);
89001b91ac2SMark Cave-Ayland 
89155d7bfe2SMark Cave-Ayland     /* 2/STIP : Stippler */
892b21de199SThomas Huth     memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
89355d7bfe2SMark Cave-Ayland                           TCX_STIP_NREGS);
89455d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->stip);
89555d7bfe2SMark Cave-Ayland 
89655d7bfe2SMark Cave-Ayland     /* 3/BLIT : Blitter */
897b21de199SThomas Huth     memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
89855d7bfe2SMark Cave-Ayland                           TCX_BLIT_NREGS);
89955d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->blit);
90055d7bfe2SMark Cave-Ayland 
90155d7bfe2SMark Cave-Ayland     /* 5/RSTIP : Raw Stippler */
902b21de199SThomas Huth     memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
90355d7bfe2SMark Cave-Ayland                           TCX_RSTIP_NREGS);
90455d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->rstip);
90555d7bfe2SMark Cave-Ayland 
90655d7bfe2SMark Cave-Ayland     /* 6/RBLIT : Raw Blitter */
907b21de199SThomas Huth     memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
90855d7bfe2SMark Cave-Ayland                           TCX_RBLIT_NREGS);
90955d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->rblit);
91055d7bfe2SMark Cave-Ayland 
91155d7bfe2SMark Cave-Ayland     /* 7/TEC : ??? */
912b21de199SThomas Huth     memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
913b21de199SThomas Huth                           TCX_TEC_NREGS);
91455d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->tec);
91555d7bfe2SMark Cave-Ayland 
91655d7bfe2SMark Cave-Ayland     /* 8/CMAP : DAC */
917b21de199SThomas Huth     memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
918b21de199SThomas Huth                           TCX_DAC_NREGS);
91901b91ac2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->dac);
92001b91ac2SMark Cave-Ayland 
92155d7bfe2SMark Cave-Ayland     /* 9/THC : Cursor */
922b21de199SThomas Huth     memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
92355d7bfe2SMark Cave-Ayland                           TCX_THC_NREGS);
92455d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->thc);
92501b91ac2SMark Cave-Ayland 
92655d7bfe2SMark Cave-Ayland     /* 11/DHC : ??? */
927b21de199SThomas Huth     memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
92855d7bfe2SMark Cave-Ayland                           TCX_DHC_NREGS);
92955d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->dhc);
93055d7bfe2SMark Cave-Ayland 
93155d7bfe2SMark Cave-Ayland     /* 12/ALT : ??? */
932b21de199SThomas Huth     memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
93355d7bfe2SMark Cave-Ayland                           TCX_ALT_NREGS);
93455d7bfe2SMark Cave-Ayland     sysbus_init_mmio(sbd, &s->alt);
93501b91ac2SMark Cave-Ayland }
93601b91ac2SMark Cave-Ayland 
937d4ad9decSMark Cave-Ayland static void tcx_realizefn(DeviceState *dev, Error **errp)
938f40070c3SBlue Swirl {
939d4ad9decSMark Cave-Ayland     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
94001774ddbSAndreas Färber     TCXState *s = TCX(dev);
941d08151bfSAvi Kivity     ram_addr_t vram_offset = 0;
942da87dd7bSMark Cave-Ayland     int size, ret;
943dc828ca1Spbrook     uint8_t *vram_base;
944da87dd7bSMark Cave-Ayland     char *fcode_filename;
945dc828ca1Spbrook 
9463eadad55SPaolo Bonzini     memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
947f8ed85acSMarkus Armbruster                            s->vram_size * (1 + 4 + 4), &error_fatal);
948c5705a77SAvi Kivity     vmstate_register_ram_global(&s->vram_mem);
94974259ae5SPaolo Bonzini     memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
950d08151bfSAvi Kivity     vram_base = memory_region_get_ram_ptr(&s->vram_mem);
951e80cfcfcSbellard 
95255d7bfe2SMark Cave-Ayland     /* 10/ROM : FCode ROM */
953da87dd7bSMark Cave-Ayland     vmstate_register_ram_global(&s->rom);
954da87dd7bSMark Cave-Ayland     fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
955da87dd7bSMark Cave-Ayland     if (fcode_filename) {
956da87dd7bSMark Cave-Ayland         ret = load_image_targphys(fcode_filename, s->prom_addr,
957da87dd7bSMark Cave-Ayland                                   FCODE_MAX_ROM_SIZE);
9588684e85cSShannon Zhao         g_free(fcode_filename);
959da87dd7bSMark Cave-Ayland         if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
960d4ad9decSMark Cave-Ayland             error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
961da87dd7bSMark Cave-Ayland         }
962da87dd7bSMark Cave-Ayland     }
963da87dd7bSMark Cave-Ayland 
96455d7bfe2SMark Cave-Ayland     /* 0/DFB8 : 8-bit plane */
965eee0b836Sblueswir1     s->vram = vram_base;
966ee6847d1SGerd Hoffmann     size = s->vram_size;
9673eadad55SPaolo Bonzini     memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
968d08151bfSAvi Kivity                              &s->vram_mem, vram_offset, size);
969d4ad9decSMark Cave-Ayland     sysbus_init_mmio(sbd, &s->vram_8bit);
970eee0b836Sblueswir1     vram_offset += size;
971eee0b836Sblueswir1     vram_base += size;
972eee0b836Sblueswir1 
97355d7bfe2SMark Cave-Ayland     /* 1/DFB24 : 24bit plane */
974ee6847d1SGerd Hoffmann     size = s->vram_size * 4;
975eee0b836Sblueswir1     s->vram24 = (uint32_t *)vram_base;
976eee0b836Sblueswir1     s->vram24_offset = vram_offset;
9773eadad55SPaolo Bonzini     memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
978d08151bfSAvi Kivity                              &s->vram_mem, vram_offset, size);
979d4ad9decSMark Cave-Ayland     sysbus_init_mmio(sbd, &s->vram_24bit);
980eee0b836Sblueswir1     vram_offset += size;
981eee0b836Sblueswir1     vram_base += size;
982eee0b836Sblueswir1 
98355d7bfe2SMark Cave-Ayland     /* 4/RDFB32 : Raw Framebuffer */
984ee6847d1SGerd Hoffmann     size = s->vram_size * 4;
985eee0b836Sblueswir1     s->cplane = (uint32_t *)vram_base;
986eee0b836Sblueswir1     s->cplane_offset = vram_offset;
9873eadad55SPaolo Bonzini     memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
988d08151bfSAvi Kivity                              &s->vram_mem, vram_offset, size);
989d4ad9decSMark Cave-Ayland     sysbus_init_mmio(sbd, &s->vram_cplane);
990f40070c3SBlue Swirl 
99155d7bfe2SMark Cave-Ayland     /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
99255d7bfe2SMark Cave-Ayland     if (s->depth == 8) {
99355d7bfe2SMark Cave-Ayland         memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
99455d7bfe2SMark Cave-Ayland                               "tcx.thc24", TCX_THC_NREGS);
99555d7bfe2SMark Cave-Ayland         sysbus_init_mmio(sbd, &s->thc24);
996eee0b836Sblueswir1     }
997eee0b836Sblueswir1 
99855d7bfe2SMark Cave-Ayland     sysbus_init_irq(sbd, &s->irq);
99955d7bfe2SMark Cave-Ayland 
100055d7bfe2SMark Cave-Ayland     if (s->depth == 8) {
100155d7bfe2SMark Cave-Ayland         s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s);
100255d7bfe2SMark Cave-Ayland     } else {
100355d7bfe2SMark Cave-Ayland         s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s);
100455d7bfe2SMark Cave-Ayland     }
100555d7bfe2SMark Cave-Ayland     s->thcmisc = 0;
100655d7bfe2SMark Cave-Ayland 
1007c78f7137SGerd Hoffmann     qemu_console_resize(s->con, s->width, s->height);
1008420557e8Sbellard }
1009420557e8Sbellard 
1010999e12bbSAnthony Liguori static Property tcx_properties[] = {
1011c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
101253dad499SGerd Hoffmann     DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
101353dad499SGerd Hoffmann     DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
101453dad499SGerd Hoffmann     DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
1015c7bcc85dSPaolo Bonzini     DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1),
101653dad499SGerd Hoffmann     DEFINE_PROP_END_OF_LIST(),
1017999e12bbSAnthony Liguori };
1018999e12bbSAnthony Liguori 
1019999e12bbSAnthony Liguori static void tcx_class_init(ObjectClass *klass, void *data)
1020999e12bbSAnthony Liguori {
102139bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
1022999e12bbSAnthony Liguori 
1023d4ad9decSMark Cave-Ayland     dc->realize = tcx_realizefn;
102439bffca2SAnthony Liguori     dc->reset = tcx_reset;
102539bffca2SAnthony Liguori     dc->vmsd = &vmstate_tcx;
102639bffca2SAnthony Liguori     dc->props = tcx_properties;
1027ee6847d1SGerd Hoffmann }
1028999e12bbSAnthony Liguori 
10298c43a6f0SAndreas Färber static const TypeInfo tcx_info = {
103001774ddbSAndreas Färber     .name          = TYPE_TCX,
103139bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
103239bffca2SAnthony Liguori     .instance_size = sizeof(TCXState),
103301b91ac2SMark Cave-Ayland     .instance_init = tcx_initfn,
1034999e12bbSAnthony Liguori     .class_init    = tcx_class_init,
1035ee6847d1SGerd Hoffmann };
1036ee6847d1SGerd Hoffmann 
103783f7d43aSAndreas Färber static void tcx_register_types(void)
1038f40070c3SBlue Swirl {
103939bffca2SAnthony Liguori     type_register_static(&tcx_info);
1040f40070c3SBlue Swirl }
1041f40070c3SBlue Swirl 
104283f7d43aSAndreas Färber type_init(tcx_register_types)
1043