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 .impl = { 553 .min_access_size = 4, 554 .max_access_size = 4, 555 }, 556 .valid = { 557 .min_access_size = 4, 558 .max_access_size = 8, 559 }, 560 }; 561 562 static const MemoryRegionOps tcx_rstip_ops = { 563 .read = tcx_stip_readl, 564 .write = tcx_rstip_writel, 565 .endianness = DEVICE_NATIVE_ENDIAN, 566 .impl = { 567 .min_access_size = 4, 568 .max_access_size = 4, 569 }, 570 .valid = { 571 .min_access_size = 4, 572 .max_access_size = 8, 573 }, 574 }; 575 576 static uint64_t tcx_blit_readl(void *opaque, hwaddr addr, 577 unsigned size) 578 { 579 return 0; 580 } 581 582 static void tcx_blit_writel(void *opaque, hwaddr addr, 583 uint64_t val, unsigned size) 584 { 585 TCXState *s = opaque; 586 uint32_t adsr, len; 587 int i; 588 589 if (!(addr & 4)) { 590 s->tmpblit = val; 591 } else { 592 addr = (addr >> 3) & 0xfffff; 593 adsr = val & 0xffffff; 594 len = ((val >> 24) & 0x1f) + 1; 595 if (adsr == 0xffffff) { 596 memset(&s->vram[addr], s->tmpblit, len); 597 if (s->depth == 24) { 598 val = s->tmpblit & 0xffffff; 599 val = cpu_to_be32(val); 600 for (i = 0; i < len; i++) { 601 s->vram24[addr + i] = val; 602 } 603 } 604 } else { 605 memcpy(&s->vram[addr], &s->vram[adsr], len); 606 if (s->depth == 24) { 607 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 608 } 609 } 610 tcx_set_dirty(s, addr, len); 611 } 612 } 613 614 static void tcx_rblit_writel(void *opaque, hwaddr addr, 615 uint64_t val, unsigned size) 616 { 617 TCXState *s = opaque; 618 uint32_t adsr, len; 619 int i; 620 621 if (!(addr & 4)) { 622 s->tmpblit = val; 623 } else { 624 addr = (addr >> 3) & 0xfffff; 625 adsr = val & 0xffffff; 626 len = ((val >> 24) & 0x1f) + 1; 627 if (adsr == 0xffffff) { 628 memset(&s->vram[addr], s->tmpblit, len); 629 if (s->depth == 24) { 630 val = s->tmpblit & 0xffffff; 631 val = cpu_to_be32(val); 632 for (i = 0; i < len; i++) { 633 s->vram24[addr + i] = val; 634 s->cplane[addr + i] = val; 635 } 636 } 637 } else { 638 memcpy(&s->vram[addr], &s->vram[adsr], len); 639 if (s->depth == 24) { 640 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 641 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); 642 } 643 } 644 tcx_set_dirty(s, addr, len); 645 } 646 } 647 648 static const MemoryRegionOps tcx_blit_ops = { 649 .read = tcx_blit_readl, 650 .write = tcx_blit_writel, 651 .endianness = DEVICE_NATIVE_ENDIAN, 652 .valid = { 653 .min_access_size = 4, 654 .max_access_size = 4, 655 }, 656 }; 657 658 static const MemoryRegionOps tcx_rblit_ops = { 659 .read = tcx_blit_readl, 660 .write = tcx_rblit_writel, 661 .endianness = DEVICE_NATIVE_ENDIAN, 662 .impl = { 663 .min_access_size = 4, 664 .max_access_size = 4, 665 }, 666 .valid = { 667 .min_access_size = 4, 668 .max_access_size = 8, 669 }, 670 }; 671 672 static void tcx_invalidate_cursor_position(TCXState *s) 673 { 674 int ymin, ymax, start, end; 675 676 /* invalidate only near the cursor */ 677 ymin = s->cursy; 678 if (ymin >= s->height) { 679 return; 680 } 681 ymax = MIN(s->height, ymin + 32); 682 start = ymin * 1024; 683 end = ymax * 1024; 684 685 tcx_set_dirty(s, start, end - start); 686 } 687 688 static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, 689 unsigned size) 690 { 691 TCXState *s = opaque; 692 uint64_t val; 693 694 if (addr == TCX_THC_MISC) { 695 val = s->thcmisc | 0x02000000; 696 } else { 697 val = 0; 698 } 699 return val; 700 } 701 702 static void tcx_thc_writel(void *opaque, hwaddr addr, 703 uint64_t val, unsigned size) 704 { 705 TCXState *s = opaque; 706 707 if (addr == TCX_THC_CURSXY) { 708 tcx_invalidate_cursor_position(s); 709 s->cursx = val >> 16; 710 s->cursy = val; 711 tcx_invalidate_cursor_position(s); 712 } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) { 713 s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val; 714 tcx_invalidate_cursor_position(s); 715 } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) { 716 s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val; 717 tcx_invalidate_cursor_position(s); 718 } else if (addr == TCX_THC_MISC) { 719 s->thcmisc = val; 720 } 721 722 } 723 724 static const MemoryRegionOps tcx_thc_ops = { 725 .read = tcx_thc_readl, 726 .write = tcx_thc_writel, 727 .endianness = DEVICE_NATIVE_ENDIAN, 728 .valid = { 729 .min_access_size = 4, 730 .max_access_size = 4, 731 }, 732 }; 733 734 static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, 735 unsigned size) 736 { 737 return 0; 738 } 739 740 static void tcx_dummy_writel(void *opaque, hwaddr addr, 741 uint64_t val, unsigned size) 742 { 743 return; 744 } 745 746 static const MemoryRegionOps tcx_dummy_ops = { 747 .read = tcx_dummy_readl, 748 .write = tcx_dummy_writel, 749 .endianness = DEVICE_NATIVE_ENDIAN, 750 .valid = { 751 .min_access_size = 4, 752 .max_access_size = 4, 753 }, 754 }; 755 756 static const GraphicHwOps tcx_ops = { 757 .invalidate = tcx_invalidate_display, 758 .gfx_update = tcx_update_display, 759 }; 760 761 static const GraphicHwOps tcx24_ops = { 762 .invalidate = tcx24_invalidate_display, 763 .gfx_update = tcx24_update_display, 764 }; 765 766 static void tcx_initfn(Object *obj) 767 { 768 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 769 TCXState *s = TCX(obj); 770 771 memory_region_init_rom_nomigrate(&s->rom, obj, "tcx.prom", 772 FCODE_MAX_ROM_SIZE, &error_fatal); 773 sysbus_init_mmio(sbd, &s->rom); 774 775 /* 2/STIP : Stippler */ 776 memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip", 777 TCX_STIP_NREGS); 778 sysbus_init_mmio(sbd, &s->stip); 779 780 /* 3/BLIT : Blitter */ 781 memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit", 782 TCX_BLIT_NREGS); 783 sysbus_init_mmio(sbd, &s->blit); 784 785 /* 5/RSTIP : Raw Stippler */ 786 memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip", 787 TCX_RSTIP_NREGS); 788 sysbus_init_mmio(sbd, &s->rstip); 789 790 /* 6/RBLIT : Raw Blitter */ 791 memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit", 792 TCX_RBLIT_NREGS); 793 sysbus_init_mmio(sbd, &s->rblit); 794 795 /* 7/TEC : ??? */ 796 memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec", 797 TCX_TEC_NREGS); 798 sysbus_init_mmio(sbd, &s->tec); 799 800 /* 8/CMAP : DAC */ 801 memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac", 802 TCX_DAC_NREGS); 803 sysbus_init_mmio(sbd, &s->dac); 804 805 /* 9/THC : Cursor */ 806 memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc", 807 TCX_THC_NREGS); 808 sysbus_init_mmio(sbd, &s->thc); 809 810 /* 11/DHC : ??? */ 811 memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc", 812 TCX_DHC_NREGS); 813 sysbus_init_mmio(sbd, &s->dhc); 814 815 /* 12/ALT : ??? */ 816 memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt", 817 TCX_ALT_NREGS); 818 sysbus_init_mmio(sbd, &s->alt); 819 } 820 821 static void tcx_realizefn(DeviceState *dev, Error **errp) 822 { 823 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 824 TCXState *s = TCX(dev); 825 ram_addr_t vram_offset = 0; 826 int size, ret; 827 uint8_t *vram_base; 828 char *fcode_filename; 829 830 memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram", 831 s->vram_size * (1 + 4 + 4), &error_fatal); 832 vmstate_register_ram_global(&s->vram_mem); 833 memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); 834 vram_base = memory_region_get_ram_ptr(&s->vram_mem); 835 836 /* 10/ROM : FCode ROM */ 837 vmstate_register_ram_global(&s->rom); 838 fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 839 if (fcode_filename) { 840 ret = load_image_mr(fcode_filename, &s->rom); 841 g_free(fcode_filename); 842 if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 843 warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE); 844 } 845 } 846 847 /* 0/DFB8 : 8-bit plane */ 848 s->vram = vram_base; 849 size = s->vram_size; 850 memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 851 &s->vram_mem, vram_offset, size); 852 sysbus_init_mmio(sbd, &s->vram_8bit); 853 vram_offset += size; 854 vram_base += size; 855 856 /* 1/DFB24 : 24bit plane */ 857 size = s->vram_size * 4; 858 s->vram24 = (uint32_t *)vram_base; 859 s->vram24_offset = vram_offset; 860 memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 861 &s->vram_mem, vram_offset, size); 862 sysbus_init_mmio(sbd, &s->vram_24bit); 863 vram_offset += size; 864 vram_base += size; 865 866 /* 4/RDFB32 : Raw Framebuffer */ 867 size = s->vram_size * 4; 868 s->cplane = (uint32_t *)vram_base; 869 s->cplane_offset = vram_offset; 870 memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 871 &s->vram_mem, vram_offset, size); 872 sysbus_init_mmio(sbd, &s->vram_cplane); 873 874 /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */ 875 if (s->depth == 8) { 876 memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s, 877 "tcx.thc24", TCX_THC_NREGS); 878 sysbus_init_mmio(sbd, &s->thc24); 879 } 880 881 sysbus_init_irq(sbd, &s->irq); 882 883 if (s->depth == 8) { 884 s->con = graphic_console_init(dev, 0, &tcx_ops, s); 885 } else { 886 s->con = graphic_console_init(dev, 0, &tcx24_ops, s); 887 } 888 s->thcmisc = 0; 889 890 qemu_console_resize(s->con, s->width, s->height); 891 } 892 893 static Property tcx_properties[] = { 894 DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 895 DEFINE_PROP_UINT16("width", TCXState, width, -1), 896 DEFINE_PROP_UINT16("height", TCXState, height, -1), 897 DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 898 DEFINE_PROP_END_OF_LIST(), 899 }; 900 901 static void tcx_class_init(ObjectClass *klass, void *data) 902 { 903 DeviceClass *dc = DEVICE_CLASS(klass); 904 905 dc->realize = tcx_realizefn; 906 dc->reset = tcx_reset; 907 dc->vmsd = &vmstate_tcx; 908 device_class_set_props(dc, tcx_properties); 909 } 910 911 static const TypeInfo tcx_info = { 912 .name = TYPE_TCX, 913 .parent = TYPE_SYS_BUS_DEVICE, 914 .instance_size = sizeof(TCXState), 915 .instance_init = tcx_initfn, 916 .class_init = tcx_class_init, 917 }; 918 919 static void tcx_register_types(void) 920 { 921 type_register_static(&tcx_info); 922 } 923 924 type_init(tcx_register_types) 925