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" 2883c9f4caSPaolo Bonzini #include "hw/sysbus.h" 29420557e8Sbellard 30420557e8Sbellard #define MAXX 1024 31420557e8Sbellard #define MAXY 768 326f7e9aecSbellard #define TCX_DAC_NREGS 16 338508b89eSblueswir1 #define TCX_THC_NREGS_8 0x081c 348508b89eSblueswir1 #define TCX_THC_NREGS_24 0x1000 358508b89eSblueswir1 #define TCX_TEC_NREGS 0x1000 36420557e8Sbellard 37420557e8Sbellard typedef struct TCXState { 38f40070c3SBlue Swirl SysBusDevice busdev; 39c78f7137SGerd Hoffmann QemuConsole *con; 408d5f07faSbellard uint8_t *vram; 41eee0b836Sblueswir1 uint32_t *vram24, *cplane; 42d08151bfSAvi Kivity MemoryRegion vram_mem; 43d08151bfSAvi Kivity MemoryRegion vram_8bit; 44d08151bfSAvi Kivity MemoryRegion vram_24bit; 45d08151bfSAvi Kivity MemoryRegion vram_cplane; 46d08151bfSAvi Kivity MemoryRegion dac; 47d08151bfSAvi Kivity MemoryRegion tec; 48d08151bfSAvi Kivity MemoryRegion thc24; 49d08151bfSAvi Kivity MemoryRegion thc8; 50d08151bfSAvi Kivity ram_addr_t vram24_offset, cplane_offset; 51ee6847d1SGerd Hoffmann uint32_t vram_size; 5221206a10Sbellard uint32_t palette[256]; 53427a66c3SBlue Swirl uint8_t r[256], g[256], b[256]; 54427a66c3SBlue Swirl uint16_t width, height, depth; 556f7e9aecSbellard uint8_t dac_index, dac_state; 56420557e8Sbellard } TCXState; 57420557e8Sbellard 58d3ffcafeSBlue Swirl static void tcx_set_dirty(TCXState *s) 59d3ffcafeSBlue Swirl { 60fd4aa979SBlue Swirl memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); 61d3ffcafeSBlue Swirl } 62d3ffcafeSBlue Swirl 63d3ffcafeSBlue Swirl static void tcx24_set_dirty(TCXState *s) 64d3ffcafeSBlue Swirl { 65fd4aa979SBlue Swirl memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4); 66fd4aa979SBlue Swirl memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4); 67d3ffcafeSBlue Swirl } 6895219897Spbrook 6921206a10Sbellard static void update_palette_entries(TCXState *s, int start, int end) 7021206a10Sbellard { 71c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 7221206a10Sbellard int i; 73c78f7137SGerd Hoffmann 7421206a10Sbellard for (i = start; i < end; i++) { 75c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 7621206a10Sbellard default: 7721206a10Sbellard case 8: 7821206a10Sbellard s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); 7921206a10Sbellard break; 8021206a10Sbellard case 15: 8121206a10Sbellard s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); 8221206a10Sbellard break; 8321206a10Sbellard case 16: 8421206a10Sbellard s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); 8521206a10Sbellard break; 8621206a10Sbellard case 32: 87c78f7137SGerd Hoffmann if (is_surface_bgr(surface)) { 887b5d76daSaliguori s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); 89c78f7137SGerd Hoffmann } else { 9021206a10Sbellard s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 91c78f7137SGerd Hoffmann } 9221206a10Sbellard break; 9321206a10Sbellard } 9421206a10Sbellard } 95d3ffcafeSBlue Swirl if (s->depth == 24) { 96d3ffcafeSBlue Swirl tcx24_set_dirty(s); 97d3ffcafeSBlue Swirl } else { 98d3ffcafeSBlue Swirl tcx_set_dirty(s); 99d3ffcafeSBlue Swirl } 10021206a10Sbellard } 10121206a10Sbellard 102e80cfcfcSbellard static void tcx_draw_line32(TCXState *s1, uint8_t *d, 103e80cfcfcSbellard const uint8_t *s, int width) 104420557e8Sbellard { 105e80cfcfcSbellard int x; 106e80cfcfcSbellard uint8_t val; 1078bdc2159Sths uint32_t *p = (uint32_t *)d; 108e80cfcfcSbellard 109e80cfcfcSbellard for(x = 0; x < width; x++) { 110e80cfcfcSbellard val = *s++; 1118bdc2159Sths *p++ = s1->palette[val]; 112e80cfcfcSbellard } 113420557e8Sbellard } 114420557e8Sbellard 11521206a10Sbellard static void tcx_draw_line16(TCXState *s1, uint8_t *d, 116e80cfcfcSbellard const uint8_t *s, int width) 117e80cfcfcSbellard { 118e80cfcfcSbellard int x; 119e80cfcfcSbellard uint8_t val; 1208bdc2159Sths uint16_t *p = (uint16_t *)d; 1218d5f07faSbellard 122e80cfcfcSbellard for(x = 0; x < width; x++) { 123e80cfcfcSbellard val = *s++; 1248bdc2159Sths *p++ = s1->palette[val]; 125e80cfcfcSbellard } 126e80cfcfcSbellard } 127e80cfcfcSbellard 128e80cfcfcSbellard static void tcx_draw_line8(TCXState *s1, uint8_t *d, 129e80cfcfcSbellard const uint8_t *s, int width) 130e80cfcfcSbellard { 131e80cfcfcSbellard int x; 132e80cfcfcSbellard uint8_t val; 133e80cfcfcSbellard 134e80cfcfcSbellard for(x = 0; x < width; x++) { 135e80cfcfcSbellard val = *s++; 13621206a10Sbellard *d++ = s1->palette[val]; 137e80cfcfcSbellard } 138e80cfcfcSbellard } 139e80cfcfcSbellard 140688ea2ebSblueswir1 /* 141688ea2ebSblueswir1 XXX Could be much more optimal: 142688ea2ebSblueswir1 * detect if line/page/whole screen is in 24 bit mode 143688ea2ebSblueswir1 * if destination is also BGR, use memcpy 144688ea2ebSblueswir1 */ 145eee0b836Sblueswir1 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 146eee0b836Sblueswir1 const uint8_t *s, int width, 147eee0b836Sblueswir1 const uint32_t *cplane, 148eee0b836Sblueswir1 const uint32_t *s24) 149eee0b836Sblueswir1 { 150c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s1->con); 1517b5d76daSaliguori int x, bgr, r, g, b; 152688ea2ebSblueswir1 uint8_t val, *p8; 153eee0b836Sblueswir1 uint32_t *p = (uint32_t *)d; 154eee0b836Sblueswir1 uint32_t dval; 155eee0b836Sblueswir1 156c78f7137SGerd Hoffmann bgr = is_surface_bgr(surface); 157eee0b836Sblueswir1 for(x = 0; x < width; x++, s++, s24++) { 158688ea2ebSblueswir1 if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) { 159688ea2ebSblueswir1 // 24-bit direct, BGR order 160688ea2ebSblueswir1 p8 = (uint8_t *)s24; 161688ea2ebSblueswir1 p8++; 162688ea2ebSblueswir1 b = *p8++; 163688ea2ebSblueswir1 g = *p8++; 164f7e683b8SBlue Swirl r = *p8; 1657b5d76daSaliguori if (bgr) 1667b5d76daSaliguori dval = rgb_to_pixel32bgr(r, g, b); 1677b5d76daSaliguori else 168688ea2ebSblueswir1 dval = rgb_to_pixel32(r, g, b); 169eee0b836Sblueswir1 } else { 170eee0b836Sblueswir1 val = *s; 171eee0b836Sblueswir1 dval = s1->palette[val]; 172eee0b836Sblueswir1 } 173eee0b836Sblueswir1 *p++ = dval; 174eee0b836Sblueswir1 } 175eee0b836Sblueswir1 } 176eee0b836Sblueswir1 177d08151bfSAvi Kivity static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24, 178c227f099SAnthony Liguori ram_addr_t cpage) 179eee0b836Sblueswir1 { 180eee0b836Sblueswir1 int ret; 181eee0b836Sblueswir1 182cd7a45c9SBlue Swirl ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, 183d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 184cd7a45c9SBlue Swirl ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, 185d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 186cd7a45c9SBlue Swirl ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, 187cd7a45c9SBlue Swirl DIRTY_MEMORY_VGA); 188eee0b836Sblueswir1 return ret; 189eee0b836Sblueswir1 } 190eee0b836Sblueswir1 191c227f099SAnthony Liguori static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, 192c227f099SAnthony Liguori ram_addr_t page_max, ram_addr_t page24, 193c227f099SAnthony Liguori ram_addr_t cpage) 194eee0b836Sblueswir1 { 195d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 196f10acc8bSMark Cave-Ayland page_min, 197f10acc8bSMark Cave-Ayland (page_max - page_min) + TARGET_PAGE_SIZE, 198d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 199d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 200d08151bfSAvi Kivity page24 + page_min * 4, 201f10acc8bSMark Cave-Ayland (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 202d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 203d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 204d08151bfSAvi Kivity cpage + page_min * 4, 205f10acc8bSMark Cave-Ayland (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 206d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 207eee0b836Sblueswir1 } 208eee0b836Sblueswir1 209e80cfcfcSbellard /* Fixed line length 1024 allows us to do nice tricks not possible on 210e80cfcfcSbellard VGA... */ 21195219897Spbrook static void tcx_update_display(void *opaque) 212e80cfcfcSbellard { 213e80cfcfcSbellard TCXState *ts = opaque; 214c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 215c227f099SAnthony Liguori ram_addr_t page, page_min, page_max; 216550be127Sbellard int y, y_start, dd, ds; 217e80cfcfcSbellard uint8_t *d, *s; 218b3ceef24Sblueswir1 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); 219e80cfcfcSbellard 220c78f7137SGerd Hoffmann if (surface_bits_per_pixel(surface) == 0) { 221e80cfcfcSbellard return; 222c78f7137SGerd Hoffmann } 223c78f7137SGerd Hoffmann 224d08151bfSAvi Kivity page = 0; 225e80cfcfcSbellard y_start = -1; 226c0c440f3SBlue Swirl page_min = -1; 227550be127Sbellard page_max = 0; 228c78f7137SGerd Hoffmann d = surface_data(surface); 2296f7e9aecSbellard s = ts->vram; 230c78f7137SGerd Hoffmann dd = surface_stride(surface); 231e80cfcfcSbellard ds = 1024; 232e80cfcfcSbellard 233c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 234e80cfcfcSbellard case 32: 235e80cfcfcSbellard f = tcx_draw_line32; 236e80cfcfcSbellard break; 23721206a10Sbellard case 15: 23821206a10Sbellard case 16: 23921206a10Sbellard f = tcx_draw_line16; 240e80cfcfcSbellard break; 241e80cfcfcSbellard default: 242e80cfcfcSbellard case 8: 243e80cfcfcSbellard f = tcx_draw_line8; 244e80cfcfcSbellard break; 245e80cfcfcSbellard case 0: 246e80cfcfcSbellard return; 247e80cfcfcSbellard } 248e80cfcfcSbellard 2496f7e9aecSbellard for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { 250cd7a45c9SBlue Swirl if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, 251cd7a45c9SBlue Swirl DIRTY_MEMORY_VGA)) { 252e80cfcfcSbellard if (y_start < 0) 253e80cfcfcSbellard y_start = y; 254e80cfcfcSbellard if (page < page_min) 255e80cfcfcSbellard page_min = page; 256e80cfcfcSbellard if (page > page_max) 257e80cfcfcSbellard page_max = page; 2586f7e9aecSbellard f(ts, d, s, ts->width); 259e80cfcfcSbellard d += dd; 260e80cfcfcSbellard s += ds; 2616f7e9aecSbellard f(ts, d, s, ts->width); 262e80cfcfcSbellard d += dd; 263e80cfcfcSbellard s += ds; 2646f7e9aecSbellard f(ts, d, s, ts->width); 265e80cfcfcSbellard d += dd; 266e80cfcfcSbellard s += ds; 2676f7e9aecSbellard f(ts, d, s, ts->width); 268e80cfcfcSbellard d += dd; 269e80cfcfcSbellard s += ds; 270e80cfcfcSbellard } else { 271e80cfcfcSbellard if (y_start >= 0) { 272e80cfcfcSbellard /* flush to display */ 273c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 2746f7e9aecSbellard ts->width, y - y_start); 275e80cfcfcSbellard y_start = -1; 276e80cfcfcSbellard } 277e80cfcfcSbellard d += dd * 4; 278e80cfcfcSbellard s += ds * 4; 279e80cfcfcSbellard } 280e80cfcfcSbellard } 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 } 286e80cfcfcSbellard /* reset modified pages */ 287c0c440f3SBlue Swirl if (page_max >= page_min) { 288d08151bfSAvi Kivity memory_region_reset_dirty(&ts->vram_mem, 289f10acc8bSMark Cave-Ayland page_min, 290f10acc8bSMark Cave-Ayland (page_max - page_min) + TARGET_PAGE_SIZE, 291d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 292e80cfcfcSbellard } 293e80cfcfcSbellard } 294e80cfcfcSbellard 295eee0b836Sblueswir1 static void tcx24_update_display(void *opaque) 296eee0b836Sblueswir1 { 297eee0b836Sblueswir1 TCXState *ts = opaque; 298c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(ts->con); 299c227f099SAnthony Liguori ram_addr_t page, page_min, page_max, cpage, page24; 300eee0b836Sblueswir1 int y, y_start, dd, ds; 301eee0b836Sblueswir1 uint8_t *d, *s; 302eee0b836Sblueswir1 uint32_t *cptr, *s24; 303eee0b836Sblueswir1 304c78f7137SGerd Hoffmann if (surface_bits_per_pixel(surface) != 32) { 305eee0b836Sblueswir1 return; 306c78f7137SGerd Hoffmann } 307c78f7137SGerd Hoffmann 308d08151bfSAvi Kivity page = 0; 309eee0b836Sblueswir1 page24 = ts->vram24_offset; 310eee0b836Sblueswir1 cpage = ts->cplane_offset; 311eee0b836Sblueswir1 y_start = -1; 312c0c440f3SBlue Swirl page_min = -1; 313eee0b836Sblueswir1 page_max = 0; 314c78f7137SGerd Hoffmann d = surface_data(surface); 315eee0b836Sblueswir1 s = ts->vram; 316eee0b836Sblueswir1 s24 = ts->vram24; 317eee0b836Sblueswir1 cptr = ts->cplane; 318c78f7137SGerd Hoffmann dd = surface_stride(surface); 319eee0b836Sblueswir1 ds = 1024; 320eee0b836Sblueswir1 321eee0b836Sblueswir1 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, 322eee0b836Sblueswir1 page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { 323d08151bfSAvi Kivity if (check_dirty(ts, page, page24, cpage)) { 324eee0b836Sblueswir1 if (y_start < 0) 325eee0b836Sblueswir1 y_start = y; 326eee0b836Sblueswir1 if (page < page_min) 327eee0b836Sblueswir1 page_min = page; 328eee0b836Sblueswir1 if (page > page_max) 329eee0b836Sblueswir1 page_max = page; 330eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 331eee0b836Sblueswir1 d += dd; 332eee0b836Sblueswir1 s += ds; 333eee0b836Sblueswir1 cptr += ds; 334eee0b836Sblueswir1 s24 += ds; 335eee0b836Sblueswir1 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 336eee0b836Sblueswir1 d += dd; 337eee0b836Sblueswir1 s += ds; 338eee0b836Sblueswir1 cptr += ds; 339eee0b836Sblueswir1 s24 += ds; 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 } else { 351eee0b836Sblueswir1 if (y_start >= 0) { 352eee0b836Sblueswir1 /* flush to display */ 353c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 354eee0b836Sblueswir1 ts->width, y - y_start); 355eee0b836Sblueswir1 y_start = -1; 356eee0b836Sblueswir1 } 357eee0b836Sblueswir1 d += dd * 4; 358eee0b836Sblueswir1 s += ds * 4; 359eee0b836Sblueswir1 cptr += ds * 4; 360eee0b836Sblueswir1 s24 += ds * 4; 361eee0b836Sblueswir1 } 362eee0b836Sblueswir1 } 363eee0b836Sblueswir1 if (y_start >= 0) { 364eee0b836Sblueswir1 /* flush to display */ 365c78f7137SGerd Hoffmann dpy_gfx_update(ts->con, 0, y_start, 366eee0b836Sblueswir1 ts->width, y - y_start); 367eee0b836Sblueswir1 } 368eee0b836Sblueswir1 /* reset modified pages */ 369c0c440f3SBlue Swirl if (page_max >= page_min) { 370eee0b836Sblueswir1 reset_dirty(ts, page_min, page_max, page24, cpage); 371eee0b836Sblueswir1 } 372eee0b836Sblueswir1 } 373eee0b836Sblueswir1 37495219897Spbrook static void tcx_invalidate_display(void *opaque) 375420557e8Sbellard { 376420557e8Sbellard TCXState *s = opaque; 377420557e8Sbellard 378d3ffcafeSBlue Swirl tcx_set_dirty(s); 379c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 380e80cfcfcSbellard } 381e80cfcfcSbellard 382eee0b836Sblueswir1 static void tcx24_invalidate_display(void *opaque) 383eee0b836Sblueswir1 { 384eee0b836Sblueswir1 TCXState *s = opaque; 385eee0b836Sblueswir1 386d3ffcafeSBlue Swirl tcx_set_dirty(s); 387d3ffcafeSBlue Swirl tcx24_set_dirty(s); 388c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 389eee0b836Sblueswir1 } 390eee0b836Sblueswir1 391e59fb374SJuan Quintela static int vmstate_tcx_post_load(void *opaque, int version_id) 392e80cfcfcSbellard { 393e80cfcfcSbellard TCXState *s = opaque; 394e80cfcfcSbellard 39521206a10Sbellard update_palette_entries(s, 0, 256); 396d3ffcafeSBlue Swirl if (s->depth == 24) { 397d3ffcafeSBlue Swirl tcx24_set_dirty(s); 398d3ffcafeSBlue Swirl } else { 399d3ffcafeSBlue Swirl tcx_set_dirty(s); 400d3ffcafeSBlue Swirl } 4015425a216Sblueswir1 402420557e8Sbellard return 0; 403420557e8Sbellard } 404420557e8Sbellard 405c0c41a4bSBlue Swirl static const VMStateDescription vmstate_tcx = { 406c0c41a4bSBlue Swirl .name ="tcx", 407c0c41a4bSBlue Swirl .version_id = 4, 408c0c41a4bSBlue Swirl .minimum_version_id = 4, 409c0c41a4bSBlue Swirl .minimum_version_id_old = 4, 410752ff2faSJuan Quintela .post_load = vmstate_tcx_post_load, 411c0c41a4bSBlue Swirl .fields = (VMStateField []) { 412c0c41a4bSBlue Swirl VMSTATE_UINT16(height, TCXState), 413c0c41a4bSBlue Swirl VMSTATE_UINT16(width, TCXState), 414c0c41a4bSBlue Swirl VMSTATE_UINT16(depth, TCXState), 415c0c41a4bSBlue Swirl VMSTATE_BUFFER(r, TCXState), 416c0c41a4bSBlue Swirl VMSTATE_BUFFER(g, TCXState), 417c0c41a4bSBlue Swirl VMSTATE_BUFFER(b, TCXState), 418c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_index, TCXState), 419c0c41a4bSBlue Swirl VMSTATE_UINT8(dac_state, TCXState), 420c0c41a4bSBlue Swirl VMSTATE_END_OF_LIST() 421c0c41a4bSBlue Swirl } 422c0c41a4bSBlue Swirl }; 423c0c41a4bSBlue Swirl 4247f23f812SMichael S. Tsirkin static void tcx_reset(DeviceState *d) 425420557e8Sbellard { 4267f23f812SMichael S. Tsirkin TCXState *s = container_of(d, TCXState, busdev.qdev); 427420557e8Sbellard 428e80cfcfcSbellard /* Initialize palette */ 429e80cfcfcSbellard memset(s->r, 0, 256); 430e80cfcfcSbellard memset(s->g, 0, 256); 431e80cfcfcSbellard memset(s->b, 0, 256); 432e80cfcfcSbellard s->r[255] = s->g[255] = s->b[255] = 255; 43321206a10Sbellard update_palette_entries(s, 0, 256); 434e80cfcfcSbellard memset(s->vram, 0, MAXX*MAXY); 435d08151bfSAvi Kivity memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 436d08151bfSAvi Kivity DIRTY_MEMORY_VGA); 4376f7e9aecSbellard s->dac_index = 0; 4386f7e9aecSbellard s->dac_state = 0; 439420557e8Sbellard } 440420557e8Sbellard 441a8170e5eSAvi Kivity static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 442d08151bfSAvi Kivity unsigned size) 4436f7e9aecSbellard { 4446f7e9aecSbellard return 0; 4456f7e9aecSbellard } 4466f7e9aecSbellard 447a8170e5eSAvi Kivity static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 448d08151bfSAvi Kivity unsigned size) 4496f7e9aecSbellard { 4506f7e9aecSbellard TCXState *s = opaque; 4516f7e9aecSbellard 452e64d7d59Sblueswir1 switch (addr) { 4536f7e9aecSbellard case 0: 4546f7e9aecSbellard s->dac_index = val >> 24; 4556f7e9aecSbellard s->dac_state = 0; 4566f7e9aecSbellard break; 457e64d7d59Sblueswir1 case 4: 4586f7e9aecSbellard switch (s->dac_state) { 4596f7e9aecSbellard case 0: 4606f7e9aecSbellard s->r[s->dac_index] = val >> 24; 46121206a10Sbellard update_palette_entries(s, s->dac_index, s->dac_index + 1); 4626f7e9aecSbellard s->dac_state++; 4636f7e9aecSbellard break; 4646f7e9aecSbellard case 1: 4656f7e9aecSbellard s->g[s->dac_index] = val >> 24; 46621206a10Sbellard update_palette_entries(s, s->dac_index, s->dac_index + 1); 4676f7e9aecSbellard s->dac_state++; 4686f7e9aecSbellard break; 4696f7e9aecSbellard case 2: 4706f7e9aecSbellard s->b[s->dac_index] = val >> 24; 47121206a10Sbellard update_palette_entries(s, s->dac_index, s->dac_index + 1); 4725c8cdbf8Sblueswir1 s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement 4736f7e9aecSbellard default: 4746f7e9aecSbellard s->dac_state = 0; 4756f7e9aecSbellard break; 4766f7e9aecSbellard } 4776f7e9aecSbellard break; 4786f7e9aecSbellard default: 4796f7e9aecSbellard break; 4806f7e9aecSbellard } 4816f7e9aecSbellard } 4826f7e9aecSbellard 483d08151bfSAvi Kivity static const MemoryRegionOps tcx_dac_ops = { 484d08151bfSAvi Kivity .read = tcx_dac_readl, 485d08151bfSAvi Kivity .write = tcx_dac_writel, 486d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 487d08151bfSAvi Kivity .valid = { 488d08151bfSAvi Kivity .min_access_size = 4, 489d08151bfSAvi Kivity .max_access_size = 4, 490d08151bfSAvi Kivity }, 4916f7e9aecSbellard }; 4926f7e9aecSbellard 493a8170e5eSAvi Kivity static uint64_t dummy_readl(void *opaque, hwaddr addr, 494d08151bfSAvi Kivity unsigned size) 4958508b89eSblueswir1 { 4968508b89eSblueswir1 return 0; 4978508b89eSblueswir1 } 4988508b89eSblueswir1 499a8170e5eSAvi Kivity static void dummy_writel(void *opaque, hwaddr addr, 500d08151bfSAvi Kivity uint64_t val, unsigned size) 5018508b89eSblueswir1 { 5028508b89eSblueswir1 } 5038508b89eSblueswir1 504d08151bfSAvi Kivity static const MemoryRegionOps dummy_ops = { 505d08151bfSAvi Kivity .read = dummy_readl, 506d08151bfSAvi Kivity .write = dummy_writel, 507d08151bfSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 508d08151bfSAvi Kivity .valid = { 509d08151bfSAvi Kivity .min_access_size = 4, 510d08151bfSAvi Kivity .max_access_size = 4, 511d08151bfSAvi Kivity }, 5128508b89eSblueswir1 }; 5138508b89eSblueswir1 514380cd056SGerd Hoffmann static const GraphicHwOps tcx_ops = { 515380cd056SGerd Hoffmann .invalidate = tcx_invalidate_display, 516380cd056SGerd Hoffmann .gfx_update = tcx_update_display, 517380cd056SGerd Hoffmann }; 518380cd056SGerd Hoffmann 519380cd056SGerd Hoffmann static const GraphicHwOps tcx24_ops = { 520380cd056SGerd Hoffmann .invalidate = tcx24_invalidate_display, 521380cd056SGerd Hoffmann .gfx_update = tcx24_update_display, 522380cd056SGerd Hoffmann }; 523380cd056SGerd Hoffmann 52481a322d4SGerd Hoffmann static int tcx_init1(SysBusDevice *dev) 525f40070c3SBlue Swirl { 526f40070c3SBlue Swirl TCXState *s = FROM_SYSBUS(TCXState, dev); 527d08151bfSAvi Kivity ram_addr_t vram_offset = 0; 528ee6847d1SGerd Hoffmann int size; 529dc828ca1Spbrook uint8_t *vram_base; 530dc828ca1Spbrook 531*3eadad55SPaolo Bonzini memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 532d08151bfSAvi Kivity s->vram_size * (1 + 4 + 4)); 533c5705a77SAvi Kivity vmstate_register_ram_global(&s->vram_mem); 534d08151bfSAvi Kivity vram_base = memory_region_get_ram_ptr(&s->vram_mem); 535e80cfcfcSbellard 536f40070c3SBlue Swirl /* 8-bit plane */ 537eee0b836Sblueswir1 s->vram = vram_base; 538ee6847d1SGerd Hoffmann size = s->vram_size; 539*3eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 540d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 541750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->vram_8bit); 542eee0b836Sblueswir1 vram_offset += size; 543eee0b836Sblueswir1 vram_base += size; 544eee0b836Sblueswir1 545f40070c3SBlue Swirl /* DAC */ 546*3eadad55SPaolo Bonzini memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s, 547*3eadad55SPaolo Bonzini "tcx.dac", TCX_DAC_NREGS); 548750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->dac); 549e80cfcfcSbellard 550f40070c3SBlue Swirl /* TEC (dummy) */ 551*3eadad55SPaolo Bonzini memory_region_init_io(&s->tec, OBJECT(s), &dummy_ops, s, 552*3eadad55SPaolo Bonzini "tcx.tec", TCX_TEC_NREGS); 553750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->tec); 554f40070c3SBlue Swirl /* THC: NetBSD writes here even with 8-bit display: dummy */ 555*3eadad55SPaolo Bonzini memory_region_init_io(&s->thc24, OBJECT(s), &dummy_ops, s, "tcx.thc24", 556d08151bfSAvi Kivity TCX_THC_NREGS_24); 557750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->thc24); 558f40070c3SBlue Swirl 559f40070c3SBlue Swirl if (s->depth == 24) { 560f40070c3SBlue Swirl /* 24-bit plane */ 561ee6847d1SGerd Hoffmann size = s->vram_size * 4; 562eee0b836Sblueswir1 s->vram24 = (uint32_t *)vram_base; 563eee0b836Sblueswir1 s->vram24_offset = vram_offset; 564*3eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 565d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 566750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->vram_24bit); 567eee0b836Sblueswir1 vram_offset += size; 568eee0b836Sblueswir1 vram_base += size; 569eee0b836Sblueswir1 570f40070c3SBlue Swirl /* Control plane */ 571ee6847d1SGerd Hoffmann size = s->vram_size * 4; 572eee0b836Sblueswir1 s->cplane = (uint32_t *)vram_base; 573eee0b836Sblueswir1 s->cplane_offset = vram_offset; 574*3eadad55SPaolo Bonzini memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 575d08151bfSAvi Kivity &s->vram_mem, vram_offset, size); 576750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->vram_cplane); 577f40070c3SBlue Swirl 578aa2beaa1SGerd Hoffmann s->con = graphic_console_init(DEVICE(dev), &tcx24_ops, s); 579eee0b836Sblueswir1 } else { 580f40070c3SBlue Swirl /* THC 8 bit (dummy) */ 581*3eadad55SPaolo Bonzini memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", 582d08151bfSAvi Kivity TCX_THC_NREGS_8); 583750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->thc8); 584f40070c3SBlue Swirl 585aa2beaa1SGerd Hoffmann s->con = graphic_console_init(DEVICE(dev), &tcx_ops, s); 586eee0b836Sblueswir1 } 587eee0b836Sblueswir1 588c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->width, s->height); 58981a322d4SGerd Hoffmann return 0; 590420557e8Sbellard } 591420557e8Sbellard 592999e12bbSAnthony Liguori static Property tcx_properties[] = { 59353dad499SGerd Hoffmann DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1), 59453dad499SGerd Hoffmann DEFINE_PROP_UINT16("width", TCXState, width, -1), 59553dad499SGerd Hoffmann DEFINE_PROP_UINT16("height", TCXState, height, -1), 59653dad499SGerd Hoffmann DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 59753dad499SGerd Hoffmann DEFINE_PROP_END_OF_LIST(), 598999e12bbSAnthony Liguori }; 599999e12bbSAnthony Liguori 600999e12bbSAnthony Liguori static void tcx_class_init(ObjectClass *klass, void *data) 601999e12bbSAnthony Liguori { 60239bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 603999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 604999e12bbSAnthony Liguori 605999e12bbSAnthony Liguori k->init = tcx_init1; 60639bffca2SAnthony Liguori dc->reset = tcx_reset; 60739bffca2SAnthony Liguori dc->vmsd = &vmstate_tcx; 60839bffca2SAnthony Liguori dc->props = tcx_properties; 609ee6847d1SGerd Hoffmann } 610999e12bbSAnthony Liguori 6118c43a6f0SAndreas Färber static const TypeInfo tcx_info = { 612999e12bbSAnthony Liguori .name = "SUNW,tcx", 61339bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 61439bffca2SAnthony Liguori .instance_size = sizeof(TCXState), 615999e12bbSAnthony Liguori .class_init = tcx_class_init, 616ee6847d1SGerd Hoffmann }; 617ee6847d1SGerd Hoffmann 61883f7d43aSAndreas Färber static void tcx_register_types(void) 619f40070c3SBlue Swirl { 62039bffca2SAnthony Liguori type_register_static(&tcx_info); 621f40070c3SBlue Swirl } 622f40070c3SBlue Swirl 62383f7d43aSAndreas Färber type_init(tcx_register_types) 624