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