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 25077805faSPaolo Bonzini #include "qemu-common.h" 2628ecbaeeSPaolo Bonzini #include "ui/console.h" 2728ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 28da87dd7bSMark Cave-Ayland #include "hw/loader.h" 2983c9f4caSPaolo Bonzini #include "hw/sysbus.h" 30420557e8Sbellard 31da87dd7bSMark Cave-Ayland #define TCX_ROM_FILE "QEMU,tcx.bin" 32da87dd7bSMark Cave-Ayland #define FCODE_MAX_ROM_SIZE 0x10000 33da87dd7bSMark Cave-Ayland 34420557e8Sbellard #define MAXX 1024 35420557e8Sbellard #define MAXY 768 366f7e9aecSbellard #define TCX_DAC_NREGS 16 378508b89eSblueswir1 #define TCX_THC_NREGS_8 0x081c 388508b89eSblueswir1 #define TCX_THC_NREGS_24 0x1000 398508b89eSblueswir1 #define TCX_TEC_NREGS 0x1000 40420557e8Sbellard 4101774ddbSAndreas Färber #define TYPE_TCX "SUNW,tcx" 4201774ddbSAndreas Färber #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX) 4301774ddbSAndreas Färber 44420557e8Sbellard typedef struct TCXState { 4501774ddbSAndreas Färber SysBusDevice parent_obj; 4601774ddbSAndreas Färber 47c78f7137SGerd Hoffmann QemuConsole *con; 488d5f07faSbellard uint8_t *vram; 49eee0b836Sblueswir1 uint32_t *vram24, *cplane; 50da87dd7bSMark Cave-Ayland hwaddr prom_addr; 51da87dd7bSMark Cave-Ayland MemoryRegion rom; 52d08151bfSAvi Kivity MemoryRegion vram_mem; 53d08151bfSAvi Kivity MemoryRegion vram_8bit; 54d08151bfSAvi Kivity MemoryRegion vram_24bit; 55d08151bfSAvi Kivity MemoryRegion vram_cplane; 56d08151bfSAvi Kivity MemoryRegion dac; 57d08151bfSAvi Kivity MemoryRegion tec; 58d08151bfSAvi Kivity MemoryRegion thc24; 59d08151bfSAvi Kivity MemoryRegion thc8; 60d08151bfSAvi Kivity ram_addr_t vram24_offset, cplane_offset; 61ee6847d1SGerd Hoffmann uint32_t vram_size; 6221206a10Sbellard uint32_t palette[256]; 63427a66c3SBlue Swirl uint8_t r[256], g[256], b[256]; 64427a66c3SBlue Swirl uint16_t width, height, depth; 656f7e9aecSbellard uint8_t dac_index, dac_state; 66420557e8Sbellard } TCXState; 67420557e8Sbellard 68d3ffcafeSBlue Swirl static void tcx_set_dirty(TCXState *s) 69d3ffcafeSBlue Swirl { 70fd4aa979SBlue Swirl memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); 71d3ffcafeSBlue Swirl } 72d3ffcafeSBlue Swirl 73d3ffcafeSBlue Swirl static void tcx24_set_dirty(TCXState *s) 74d3ffcafeSBlue Swirl { 75fd4aa979SBlue Swirl memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4); 76fd4aa979SBlue Swirl memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4); 77d3ffcafeSBlue Swirl } 7895219897Spbrook 7921206a10Sbellard static void update_palette_entries(TCXState *s, int start, int end) 8021206a10Sbellard { 81c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 8221206a10Sbellard int i; 83c78f7137SGerd Hoffmann 8421206a10Sbellard for (i = start; i < end; i++) { 85c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 8621206a10Sbellard default: 8721206a10Sbellard case 8: 8821206a10Sbellard s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); 8921206a10Sbellard break; 9021206a10Sbellard case 15: 9121206a10Sbellard s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); 9221206a10Sbellard break; 9321206a10Sbellard case 16: 9421206a10Sbellard s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); 9521206a10Sbellard break; 9621206a10Sbellard case 32: 97c78f7137SGerd Hoffmann if (is_surface_bgr(surface)) { 987b5d76daSaliguori s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); 99c78f7137SGerd Hoffmann } else { 10021206a10Sbellard s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 101c78f7137SGerd Hoffmann } 10221206a10Sbellard break; 10321206a10Sbellard } 10421206a10Sbellard } 105d3ffcafeSBlue Swirl if (s->depth == 24) { 106d3ffcafeSBlue Swirl tcx24_set_dirty(s); 107d3ffcafeSBlue Swirl } else { 108d3ffcafeSBlue Swirl tcx_set_dirty(s); 109d3ffcafeSBlue Swirl } 11021206a10Sbellard } 11121206a10Sbellard 112e80cfcfcSbellard static void tcx_draw_line32(TCXState *s1, uint8_t *d, 113e80cfcfcSbellard const uint8_t *s, int width) 114420557e8Sbellard { 115e80cfcfcSbellard int x; 116e80cfcfcSbellard uint8_t val; 1178bdc2159Sths uint32_t *p = (uint32_t *)d; 118e80cfcfcSbellard 119e80cfcfcSbellard for(x = 0; x < width; x++) { 120e80cfcfcSbellard val = *s++; 1218bdc2159Sths *p++ = s1->palette[val]; 122e80cfcfcSbellard } 123420557e8Sbellard } 124420557e8Sbellard 12521206a10Sbellard static void tcx_draw_line16(TCXState *s1, uint8_t *d, 126e80cfcfcSbellard const uint8_t *s, int width) 127e80cfcfcSbellard { 128e80cfcfcSbellard int x; 129e80cfcfcSbellard uint8_t val; 1308bdc2159Sths uint16_t *p = (uint16_t *)d; 1318d5f07faSbellard 132e80cfcfcSbellard for(x = 0; x < width; x++) { 133e80cfcfcSbellard val = *s++; 1348bdc2159Sths *p++ = s1->palette[val]; 135e80cfcfcSbellard } 136e80cfcfcSbellard } 137e80cfcfcSbellard 138e80cfcfcSbellard static void tcx_draw_line8(TCXState *s1, uint8_t *d, 139e80cfcfcSbellard const uint8_t *s, int width) 140e80cfcfcSbellard { 141e80cfcfcSbellard int x; 142e80cfcfcSbellard uint8_t val; 143e80cfcfcSbellard 144e80cfcfcSbellard for(x = 0; x < width; x++) { 145e80cfcfcSbellard val = *s++; 14621206a10Sbellard *d++ = s1->palette[val]; 147e80cfcfcSbellard } 148e80cfcfcSbellard } 149e80cfcfcSbellard 150688ea2ebSblueswir1 /* 151688ea2ebSblueswir1 XXX Could be much more optimal: 152688ea2ebSblueswir1 * detect if line/page/whole screen is in 24 bit mode 153688ea2ebSblueswir1 * if destination is also BGR, use memcpy 154688ea2ebSblueswir1 */ 155eee0b836Sblueswir1 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 156eee0b836Sblueswir1 const uint8_t *s, int width, 157eee0b836Sblueswir1 const uint32_t *cplane, 158eee0b836Sblueswir1 const uint32_t *s24) 159eee0b836Sblueswir1 { 160c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s1->con); 1617b5d76daSaliguori int x, bgr, r, g, b; 162688ea2ebSblueswir1 uint8_t val, *p8; 163eee0b836Sblueswir1 uint32_t *p = (uint32_t *)d; 164eee0b836Sblueswir1 uint32_t dval; 165eee0b836Sblueswir1 166c78f7137SGerd Hoffmann bgr = is_surface_bgr(surface); 167eee0b836Sblueswir1 for(x = 0; x < width; x++, s++, s24++) { 168688ea2ebSblueswir1 if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) { 169688ea2ebSblueswir1 // 24-bit direct, BGR order 170688ea2ebSblueswir1 p8 = (uint8_t *)s24; 171688ea2ebSblueswir1 p8++; 172688ea2ebSblueswir1 b = *p8++; 173688ea2ebSblueswir1 g = *p8++; 174f7e683b8SBlue Swirl r = *p8; 1757b5d76daSaliguori if (bgr) 1767b5d76daSaliguori dval = rgb_to_pixel32bgr(r, g, b); 1777b5d76daSaliguori else 178688ea2ebSblueswir1 dval = rgb_to_pixel32(r, g, b); 179eee0b836Sblueswir1 } else { 180eee0b836Sblueswir1 val = *s; 181eee0b836Sblueswir1 dval = s1->palette[val]; 182eee0b836Sblueswir1 } 183eee0b836Sblueswir1 *p++ = dval; 184eee0b836Sblueswir1 } 185eee0b836Sblueswir1 } 186eee0b836Sblueswir1 187d08151bfSAvi Kivity static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24, 188c227f099SAnthony Liguori ram_addr_t cpage) 189eee0b836Sblueswir1 { 190eee0b836Sblueswir1 int ret; 191eee0b836Sblueswir1 192cd7a45c9SBlue Swirl ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, 193d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 194cd7a45c9SBlue Swirl ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, 195d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 196cd7a45c9SBlue Swirl ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, 197cd7a45c9SBlue Swirl DIRTY_MEMORY_VGA); 198eee0b836Sblueswir1 return ret; 199eee0b836Sblueswir1 } 200eee0b836Sblueswir1 201c227f099SAnthony Liguori static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, 202c227f099SAnthony Liguori ram_addr_t page_max, ram_addr_t page24, 203c227f099SAnthony Liguori ram_addr_t cpage) 204eee0b836Sblueswir1 { 205d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 206f10acc8bSMark Cave-Ayland page_min, 207f10acc8bSMark Cave-Ayland (page_max - page_min) + TARGET_PAGE_SIZE, 208d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 209d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 210d08151bfSAvi Kivity page24 + page_min * 4, 211f10acc8bSMark Cave-Ayland (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 212d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 213d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 214d08151bfSAvi Kivity cpage + page_min * 4, 215f10acc8bSMark Cave-Ayland (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 216d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 217eee0b836Sblueswir1 } 218eee0b836Sblueswir1 219e80cfcfcSbellard /* Fixed line length 1024 allows us to do nice tricks not possible on 220e80cfcfcSbellard VGA... */ 22195219897Spbrook static void tcx_update_display(void *opaque) 222e80cfcfcSbellard { 223e80cfcfcSbellard TCXState *ts = opaque; 224c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 225c227f099SAnthony Liguori ram_addr_t page, page_min, page_max; 226550be127Sbellard int y, y_start, dd, ds; 227e80cfcfcSbellard uint8_t *d, *s; 228b3ceef24Sblueswir1 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); 229e80cfcfcSbellard 230c78f7137SGerd Hoffmann if (surface_bits_per_pixel(surface) == 0) { 231e80cfcfcSbellard return; 232c78f7137SGerd Hoffmann } 233c78f7137SGerd Hoffmann 234d08151bfSAvi Kivity page = 0; 235e80cfcfcSbellard y_start = -1; 236c0c440f3SBlue Swirl page_min = -1; 237550be127Sbellard page_max = 0; 238c78f7137SGerd Hoffmann d = surface_data(surface); 2396f7e9aecSbellard s = ts->vram; 240c78f7137SGerd Hoffmann dd = surface_stride(surface); 241e80cfcfcSbellard ds = 1024; 242e80cfcfcSbellard 243c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 244e80cfcfcSbellard case 32: 245e80cfcfcSbellard f = tcx_draw_line32; 246e80cfcfcSbellard break; 24721206a10Sbellard case 15: 24821206a10Sbellard case 16: 24921206a10Sbellard f = tcx_draw_line16; 250e80cfcfcSbellard break; 251e80cfcfcSbellard default: 252e80cfcfcSbellard case 8: 253e80cfcfcSbellard f = tcx_draw_line8; 254e80cfcfcSbellard break; 255e80cfcfcSbellard case 0: 256e80cfcfcSbellard return; 257e80cfcfcSbellard } 258e80cfcfcSbellard 2596f7e9aecSbellard for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { 260cd7a45c9SBlue Swirl if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, 261cd7a45c9SBlue Swirl DIRTY_MEMORY_VGA)) { 262e80cfcfcSbellard if (y_start < 0) 263e80cfcfcSbellard y_start = y; 264e80cfcfcSbellard if (page < page_min) 265e80cfcfcSbellard page_min = page; 266e80cfcfcSbellard if (page > page_max) 267e80cfcfcSbellard page_max = page; 2686f7e9aecSbellard f(ts, d, s, ts->width); 269e80cfcfcSbellard d += dd; 270e80cfcfcSbellard s += ds; 2716f7e9aecSbellard f(ts, d, s, ts->width); 272e80cfcfcSbellard d += dd; 273e80cfcfcSbellard s += ds; 2746f7e9aecSbellard f(ts, d, s, ts->width); 275e80cfcfcSbellard d += dd; 276e80cfcfcSbellard s += ds; 2776f7e9aecSbellard f(ts, d, s, ts->width); 278e80cfcfcSbellard d += dd; 279e80cfcfcSbellard s += ds; 280e80cfcfcSbellard } else { 281e80cfcfcSbellard if (y_start >= 0) { 282e80cfcfcSbellard /* flush to display */ 283c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2846f7e9aecSbellard ts->width, y - y_start); 285e80cfcfcSbellard y_start = -1; 286e80cfcfcSbellard } 287e80cfcfcSbellard d += dd * 4; 288e80cfcfcSbellard s += ds * 4; 289e80cfcfcSbellard } 290e80cfcfcSbellard } 291e80cfcfcSbellard if (y_start >= 0) { 292e80cfcfcSbellard /* flush to display */ 293c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2946f7e9aecSbellard ts->width, y - y_start); 295e80cfcfcSbellard } 296e80cfcfcSbellard /* reset modified pages */ 297c0c440f3SBlue Swirl if (page_max >= page_min) { 298d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 299f10acc8bSMark Cave-Ayland page_min, 300f10acc8bSMark Cave-Ayland (page_max - page_min) + TARGET_PAGE_SIZE, 301d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 302e80cfcfcSbellard } 303e80cfcfcSbellard } 304e80cfcfcSbellard 305eee0b836Sblueswir1 static void tcx24_update_display(void *opaque) 306eee0b836Sblueswir1 { 307eee0b836Sblueswir1 TCXState *ts = opaque; 308c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 309c227f099SAnthony Liguori ram_addr_t page, page_min, page_max, cpage, page24; 310eee0b836Sblueswir1 int y, y_start, dd, ds; 311eee0b836Sblueswir1 uint8_t *d, *s; 312eee0b836Sblueswir1 uint32_t *cptr, *s24; 313eee0b836Sblueswir1 314c78f7137SGerd Hoffmann if (surface_bits_per_pixel(surface) != 32) { 315eee0b836Sblueswir1 return; 316c78f7137SGerd Hoffmann } 317c78f7137SGerd Hoffmann 318d08151bfSAvi Kivity page = 0; 319eee0b836Sblueswir1 page24 = ts->vram24_offset; 320eee0b836Sblueswir1 cpage = ts->cplane_offset; 321eee0b836Sblueswir1 y_start = -1; 322c0c440f3SBlue Swirl page_min = -1; 323eee0b836Sblueswir1 page_max = 0; 324c78f7137SGerd Hoffmann d = surface_data(surface); 325eee0b836Sblueswir1 s = ts->vram; 326eee0b836Sblueswir1 s24 = ts->vram24; 327eee0b836Sblueswir1 cptr = ts->cplane; 328c78f7137SGerd Hoffmann dd = surface_stride(surface); 329eee0b836Sblueswir1 ds = 1024; 330eee0b836Sblueswir1 331eee0b836Sblueswir1 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, 332eee0b836Sblueswir1 page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { 333d08151bfSAvi Kivity if (check_dirty(ts, page, page24, cpage)) { 334eee0b836Sblueswir1 if (y_start < 0) 335eee0b836Sblueswir1 y_start = y; 336eee0b836Sblueswir1 if (page < page_min) 337eee0b836Sblueswir1 page_min = page; 338eee0b836Sblueswir1 if (page > page_max) 339eee0b836Sblueswir1 page_max = page; 340eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 341eee0b836Sblueswir1 d += dd; 342eee0b836Sblueswir1 s += ds; 343eee0b836Sblueswir1 cptr += ds; 344eee0b836Sblueswir1 s24 += ds; 345eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 346eee0b836Sblueswir1 d += dd; 347eee0b836Sblueswir1 s += ds; 348eee0b836Sblueswir1 cptr += ds; 349eee0b836Sblueswir1 s24 += ds; 350eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 351eee0b836Sblueswir1 d += dd; 352eee0b836Sblueswir1 s += ds; 353eee0b836Sblueswir1 cptr += ds; 354eee0b836Sblueswir1 s24 += ds; 355eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 356eee0b836Sblueswir1 d += dd; 357eee0b836Sblueswir1 s += ds; 358eee0b836Sblueswir1 cptr += ds; 359eee0b836Sblueswir1 s24 += ds; 360eee0b836Sblueswir1 } else { 361eee0b836Sblueswir1 if (y_start >= 0) { 362eee0b836Sblueswir1 /* flush to display */ 363c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 364eee0b836Sblueswir1 ts->width, y - y_start); 365eee0b836Sblueswir1 y_start = -1; 366eee0b836Sblueswir1 } 367eee0b836Sblueswir1 d += dd * 4; 368eee0b836Sblueswir1 s += ds * 4; 369eee0b836Sblueswir1 cptr += ds * 4; 370eee0b836Sblueswir1 s24 += ds * 4; 371eee0b836Sblueswir1 } 372eee0b836Sblueswir1 } 373eee0b836Sblueswir1 if (y_start >= 0) { 374eee0b836Sblueswir1 /* flush to display */ 375c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 376eee0b836Sblueswir1 ts->width, y - y_start); 377eee0b836Sblueswir1 } 378eee0b836Sblueswir1 /* reset modified pages */ 379c0c440f3SBlue Swirl if (page_max >= page_min) { 380eee0b836Sblueswir1 reset_dirty(ts, page_min, page_max, page24, cpage); 381eee0b836Sblueswir1 } 382eee0b836Sblueswir1 } 383eee0b836Sblueswir1 38495219897Spbrook static void tcx_invalidate_display(void *opaque) 385420557e8Sbellard { 386420557e8Sbellard TCXState *s = opaque; 387420557e8Sbellard 388d3ffcafeSBlue Swirl tcx_set_dirty(s); 389c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 390e80cfcfcSbellard } 391e80cfcfcSbellard 392eee0b836Sblueswir1 static void tcx24_invalidate_display(void *opaque) 393eee0b836Sblueswir1 { 394eee0b836Sblueswir1 TCXState *s = opaque; 395eee0b836Sblueswir1 396d3ffcafeSBlue Swirl tcx_set_dirty(s); 397d3ffcafeSBlue Swirl tcx24_set_dirty(s); 398c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 399eee0b836Sblueswir1 } 400eee0b836Sblueswir1 401e59fb374SJuan Quintela static int vmstate_tcx_post_load(void *opaque, int version_id) 402e80cfcfcSbellard { 403e80cfcfcSbellard TCXState *s = opaque; 404e80cfcfcSbellard 40521206a10Sbellard update_palette_entries(s, 0, 256); 406d3ffcafeSBlue Swirl if (s->depth == 24) { 407d3ffcafeSBlue Swirl tcx24_set_dirty(s); 408d3ffcafeSBlue Swirl } else { 409d3ffcafeSBlue Swirl tcx_set_dirty(s); 410d3ffcafeSBlue Swirl } 4115425a216Sblueswir1 412420557e8Sbellard return 0; 413420557e8Sbellard } 414420557e8Sbellard 415c0c41a4bSBlue Swirl static const VMStateDescription vmstate_tcx = { 416c0c41a4bSBlue Swirl .name ="tcx", 417c0c41a4bSBlue Swirl .version_id = 4, 418c0c41a4bSBlue Swirl .minimum_version_id = 4, 419752ff2faSJuan Quintela .post_load = vmstate_tcx_post_load, 420c0c41a4bSBlue Swirl .fields = (VMStateField[]) { 421c0c41a4bSBlue Swirl VMSTATE_UINT16(height, TCXState), 422c0c41a4bSBlue Swirl VMSTATE_UINT16(width, TCXState), 423c0c41a4bSBlue Swirl VMSTATE_UINT16(depth, TCXState), 424c0c41a4bSBlue Swirl VMSTATE_BUFFER(r, TCXState), 425c0c41a4bSBlue Swirl VMSTATE_BUFFER(g, TCXState), 426c0c41a4bSBlue Swirl VMSTATE_BUFFER(b, TCXState), 427c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_index, TCXState), 428c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_state, TCXState), 429c0c41a4bSBlue Swirl VMSTATE_END_OF_LIST() 430c0c41a4bSBlue Swirl } 431c0c41a4bSBlue Swirl }; 432c0c41a4bSBlue Swirl 4337f23f812SMichael S. Tsirkin static void tcx_reset(DeviceState *d) 434420557e8Sbellard { 43501774ddbSAndreas Färber TCXState *s = TCX(d); 436420557e8Sbellard 437e80cfcfcSbellard /* Initialize palette */ 438e80cfcfcSbellard memset(s->r, 0, 256); 439e80cfcfcSbellard memset(s->g, 0, 256); 440e80cfcfcSbellard memset(s->b, 0, 256); 441e80cfcfcSbellard s->r[255] = s->g[255] = s->b[255] = 255; 44221206a10Sbellard update_palette_entries(s, 0, 256); 443e80cfcfcSbellard memset(s->vram, 0, MAXX*MAXY); 444d08151bfSAvi Kivity memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 445d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 4466f7e9aecSbellard s->dac_index = 0; 4476f7e9aecSbellard s->dac_state = 0; 448420557e8Sbellard } 449420557e8Sbellard 450a8170e5eSAvi Kivity static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 451d08151bfSAvi Kivity unsigned size) 4526f7e9aecSbellard { 4536f7e9aecSbellard return 0; 4546f7e9aecSbellard } 4556f7e9aecSbellard 456a8170e5eSAvi Kivity static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 457d08151bfSAvi Kivity unsigned size) 4586f7e9aecSbellard { 4596f7e9aecSbellard TCXState *s = opaque; 4606f7e9aecSbellard 461e64d7d59Sblueswir1 switch (addr) { 4626f7e9aecSbellard case 0: 4636f7e9aecSbellard s->dac_index = val >> 24; 4646f7e9aecSbellard s->dac_state = 0; 4656f7e9aecSbellard break; 466e64d7d59Sblueswir1 case 4: 4676f7e9aecSbellard switch (s->dac_state) { 4686f7e9aecSbellard case 0: 4696f7e9aecSbellard s->r[s->dac_index] = val >> 24; 47021206a10Sbellard update_palette_entries(s, s->dac_index, s->dac_index + 1); 4716f7e9aecSbellard s->dac_state++; 4726f7e9aecSbellard break; 4736f7e9aecSbellard case 1: 4746f7e9aecSbellard s->g[s->dac_index] = val >> 24; 47521206a10Sbellard update_palette_entries(s, s->dac_index, s->dac_index + 1); 4766f7e9aecSbellard s->dac_state++; 4776f7e9aecSbellard break; 4786f7e9aecSbellard case 2: 4796f7e9aecSbellard s->b[s->dac_index] = val >> 24; 48021206a10Sbellard update_palette_entries(s, s->dac_index, s->dac_index + 1); 4815c8cdbf8Sblueswir1 s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement 4826f7e9aecSbellard default: 4836f7e9aecSbellard s->dac_state = 0; 4846f7e9aecSbellard break; 4856f7e9aecSbellard } 4866f7e9aecSbellard break; 4876f7e9aecSbellard default: 4886f7e9aecSbellard break; 4896f7e9aecSbellard } 4906f7e9aecSbellard } 4916f7e9aecSbellard 492d08151bfSAvi Kivity static const MemoryRegionOps tcx_dac_ops = { 493d08151bfSAvi Kivity .read = tcx_dac_readl, 494d08151bfSAvi Kivity .write = tcx_dac_writel, 495d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 496d08151bfSAvi Kivity .valid = { 497d08151bfSAvi Kivity .min_access_size = 4, 498d08151bfSAvi Kivity .max_access_size = 4, 499d08151bfSAvi Kivity }, 5006f7e9aecSbellard }; 5016f7e9aecSbellard 502a8170e5eSAvi Kivity static uint64_t dummy_readl(void *opaque, hwaddr addr, 503d08151bfSAvi Kivity unsigned size) 5048508b89eSblueswir1 { 5058508b89eSblueswir1 return 0; 5068508b89eSblueswir1 } 5078508b89eSblueswir1 508a8170e5eSAvi Kivity static void dummy_writel(void *opaque, hwaddr addr, 509d08151bfSAvi Kivity uint64_t val, unsigned size) 5108508b89eSblueswir1 { 5118508b89eSblueswir1 } 5128508b89eSblueswir1 513d08151bfSAvi Kivity static const MemoryRegionOps dummy_ops = { 514d08151bfSAvi Kivity .read = dummy_readl, 515d08151bfSAvi Kivity .write = dummy_writel, 516d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 517d08151bfSAvi Kivity .valid = { 518d08151bfSAvi Kivity .min_access_size = 4, 519d08151bfSAvi Kivity .max_access_size = 4, 520d08151bfSAvi Kivity }, 5218508b89eSblueswir1 }; 5228508b89eSblueswir1 523380cd056SGerd Hoffmann static const GraphicHwOps tcx_ops = { 524380cd056SGerd Hoffmann .invalidate = tcx_invalidate_display, 525380cd056SGerd Hoffmann .gfx_update = tcx_update_display, 526380cd056SGerd Hoffmann }; 527380cd056SGerd Hoffmann 528380cd056SGerd Hoffmann static const GraphicHwOps tcx24_ops = { 529380cd056SGerd Hoffmann .invalidate = tcx24_invalidate_display, 530380cd056SGerd Hoffmann .gfx_update = tcx24_update_display, 531380cd056SGerd Hoffmann }; 532380cd056SGerd Hoffmann 533*01b91ac2SMark Cave-Ayland static void tcx_initfn(Object *obj) 534*01b91ac2SMark Cave-Ayland { 535*01b91ac2SMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 536*01b91ac2SMark Cave-Ayland TCXState *s = TCX(obj); 537*01b91ac2SMark Cave-Ayland 538*01b91ac2SMark Cave-Ayland memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE); 539*01b91ac2SMark Cave-Ayland memory_region_set_readonly(&s->rom, true); 540*01b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->rom); 541*01b91ac2SMark Cave-Ayland 542*01b91ac2SMark Cave-Ayland /* DAC */ 543*01b91ac2SMark Cave-Ayland memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s, 544*01b91ac2SMark Cave-Ayland "tcx.dac", TCX_DAC_NREGS); 545*01b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->dac); 546*01b91ac2SMark Cave-Ayland 547*01b91ac2SMark Cave-Ayland /* TEC (dummy) */ 548*01b91ac2SMark Cave-Ayland memory_region_init_io(&s->tec, OBJECT(s), &dummy_ops, s, 549*01b91ac2SMark Cave-Ayland "tcx.tec", TCX_TEC_NREGS); 550*01b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->tec); 551*01b91ac2SMark Cave-Ayland 552*01b91ac2SMark Cave-Ayland /* THC: NetBSD writes here even with 8-bit display: dummy */ 553*01b91ac2SMark Cave-Ayland memory_region_init_io(&s->thc24, OBJECT(s), &dummy_ops, s, "tcx.thc24", 554*01b91ac2SMark Cave-Ayland TCX_THC_NREGS_24); 555*01b91ac2SMark Cave-Ayland sysbus_init_mmio(sbd, &s->thc24); 556*01b91ac2SMark Cave-Ayland 557*01b91ac2SMark Cave-Ayland return; 558*01b91ac2SMark Cave-Ayland } 559*01b91ac2SMark Cave-Ayland 560d4ad9decSMark Cave-Ayland static void tcx_realizefn(DeviceState *dev, Error **errp) 561f40070c3SBlue Swirl { 562d4ad9decSMark Cave-Ayland SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 56301774ddbSAndreas Färber TCXState *s = TCX(dev); 564d08151bfSAvi Kivity ram_addr_t vram_offset = 0; 565da87dd7bSMark Cave-Ayland int size, ret; 566dc828ca1Spbrook uint8_t *vram_base; 567da87dd7bSMark Cave-Ayland char *fcode_filename; 568dc828ca1Spbrook 5693eadad55SPaolo Bonzini memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 570d08151bfSAvi Kivity s->vram_size * (1 + 4 + 4)); 571c5705a77SAvi Kivity vmstate_register_ram_global(&s->vram_mem); 572d08151bfSAvi Kivity vram_base = memory_region_get_ram_ptr(&s->vram_mem); 573e80cfcfcSbellard 574da87dd7bSMark Cave-Ayland /* FCode ROM */ 575da87dd7bSMark Cave-Ayland vmstate_register_ram_global(&s->rom); 576da87dd7bSMark Cave-Ayland fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 577da87dd7bSMark Cave-Ayland if (fcode_filename) { 578da87dd7bSMark Cave-Ayland ret = load_image_targphys(fcode_filename, s->prom_addr, 579da87dd7bSMark Cave-Ayland FCODE_MAX_ROM_SIZE); 580da87dd7bSMark Cave-Ayland if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 581d4ad9decSMark Cave-Ayland error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); 582da87dd7bSMark Cave-Ayland } 583da87dd7bSMark Cave-Ayland } 584da87dd7bSMark Cave-Ayland 585f40070c3SBlue Swirl /* 8-bit plane */ 586eee0b836Sblueswir1 s->vram = vram_base; 587ee6847d1SGerd Hoffmann size = s->vram_size; 5883eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 589d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 590d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_8bit); 591eee0b836Sblueswir1 vram_offset += size; 592eee0b836Sblueswir1 vram_base += size; 593eee0b836Sblueswir1 594f40070c3SBlue Swirl if (s->depth == 24) { 595f40070c3SBlue Swirl /* 24-bit plane */ 596ee6847d1SGerd Hoffmann size = s->vram_size * 4; 597eee0b836Sblueswir1 s->vram24 = (uint32_t *)vram_base; 598eee0b836Sblueswir1 s->vram24_offset = vram_offset; 5993eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 600d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 601d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_24bit); 602eee0b836Sblueswir1 vram_offset += size; 603eee0b836Sblueswir1 vram_base += size; 604eee0b836Sblueswir1 605f40070c3SBlue Swirl /* Control plane */ 606ee6847d1SGerd Hoffmann size = s->vram_size * 4; 607eee0b836Sblueswir1 s->cplane = (uint32_t *)vram_base; 608eee0b836Sblueswir1 s->cplane_offset = vram_offset; 6093eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 610d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 611d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->vram_cplane); 612f40070c3SBlue Swirl 6135643706aSGerd Hoffmann s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); 614eee0b836Sblueswir1 } else { 615f40070c3SBlue Swirl /* THC 8 bit (dummy) */ 6163eadad55SPaolo Bonzini memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", 617d08151bfSAvi Kivity TCX_THC_NREGS_8); 618d4ad9decSMark Cave-Ayland sysbus_init_mmio(sbd, &s->thc8); 619f40070c3SBlue Swirl 6205643706aSGerd Hoffmann s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); 621eee0b836Sblueswir1 } 622eee0b836Sblueswir1 623c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 624420557e8Sbellard } 625420557e8Sbellard 626999e12bbSAnthony Liguori static Property tcx_properties[] = { 627c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 62853dad499SGerd Hoffmann DEFINE_PROP_UINT16("width", TCXState, width, -1), 62953dad499SGerd Hoffmann DEFINE_PROP_UINT16("height", TCXState, height, -1), 63053dad499SGerd Hoffmann DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 631c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), 63253dad499SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 633999e12bbSAnthony Liguori }; 634999e12bbSAnthony Liguori 635999e12bbSAnthony Liguori static void tcx_class_init(ObjectClass *klass, void *data) 636999e12bbSAnthony Liguori { 63739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 638999e12bbSAnthony Liguori 639d4ad9decSMark Cave-Ayland dc->realize = tcx_realizefn; 64039bffca2SAnthony Liguori dc->reset = tcx_reset; 64139bffca2SAnthony Liguori dc->vmsd = &vmstate_tcx; 64239bffca2SAnthony Liguori dc->props = tcx_properties; 643ee6847d1SGerd Hoffmann } 644999e12bbSAnthony Liguori 6458c43a6f0SAndreas Färber static const TypeInfo tcx_info = { 64601774ddbSAndreas Färber .name = TYPE_TCX, 64739bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 64839bffca2SAnthony Liguori .instance_size = sizeof(TCXState), 649*01b91ac2SMark Cave-Ayland .instance_init = tcx_initfn, 650999e12bbSAnthony Liguori .class_init = tcx_class_init, 651ee6847d1SGerd Hoffmann }; 652ee6847d1SGerd Hoffmann 65383f7d43aSAndreas Färber static void tcx_register_types(void) 654f40070c3SBlue Swirl { 65539bffca2SAnthony Liguori type_register_static(&tcx_info); 656f40070c3SBlue Swirl } 657f40070c3SBlue Swirl 65883f7d43aSAndreas Färber type_init(tcx_register_types) 659