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