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