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