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