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-common.h" 26 #include "ui/console.h" 27 #include "ui/pixel_ops.h" 28 #include "hw/loader.h" 29 #include "hw/sysbus.h" 30 31 #define TCX_ROM_FILE "QEMU,tcx.bin" 32 #define FCODE_MAX_ROM_SIZE 0x10000 33 34 #define MAXX 1024 35 #define MAXY 768 36 #define TCX_DAC_NREGS 16 37 #define TCX_THC_NREGS_8 0x081c 38 #define TCX_THC_NREGS_24 0x1000 39 #define TCX_TEC_NREGS 0x1000 40 41 #define TYPE_TCX "SUNW,tcx" 42 #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX) 43 44 typedef struct TCXState { 45 SysBusDevice parent_obj; 46 47 QemuConsole *con; 48 uint8_t *vram; 49 uint32_t *vram24, *cplane; 50 hwaddr prom_addr; 51 MemoryRegion rom; 52 MemoryRegion vram_mem; 53 MemoryRegion vram_8bit; 54 MemoryRegion vram_24bit; 55 MemoryRegion vram_cplane; 56 MemoryRegion dac; 57 MemoryRegion tec; 58 MemoryRegion thc24; 59 MemoryRegion thc8; 60 ram_addr_t vram24_offset, cplane_offset; 61 uint32_t vram_size; 62 uint32_t palette[256]; 63 uint8_t r[256], g[256], b[256]; 64 uint16_t width, height, depth; 65 uint8_t dac_index, dac_state; 66 } TCXState; 67 68 static void tcx_set_dirty(TCXState *s) 69 { 70 memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); 71 } 72 73 static void tcx24_set_dirty(TCXState *s) 74 { 75 memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4); 76 memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4); 77 } 78 79 static void update_palette_entries(TCXState *s, int start, int end) 80 { 81 DisplaySurface *surface = qemu_console_surface(s->con); 82 int i; 83 84 for (i = start; i < end; i++) { 85 switch (surface_bits_per_pixel(surface)) { 86 default: 87 case 8: 88 s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); 89 break; 90 case 15: 91 s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); 92 break; 93 case 16: 94 s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); 95 break; 96 case 32: 97 if (is_surface_bgr(surface)) { 98 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); 99 } else { 100 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 101 } 102 break; 103 } 104 } 105 if (s->depth == 24) { 106 tcx24_set_dirty(s); 107 } else { 108 tcx_set_dirty(s); 109 } 110 } 111 112 static void tcx_draw_line32(TCXState *s1, uint8_t *d, 113 const uint8_t *s, int width) 114 { 115 int x; 116 uint8_t val; 117 uint32_t *p = (uint32_t *)d; 118 119 for(x = 0; x < width; x++) { 120 val = *s++; 121 *p++ = s1->palette[val]; 122 } 123 } 124 125 static void tcx_draw_line16(TCXState *s1, uint8_t *d, 126 const uint8_t *s, int width) 127 { 128 int x; 129 uint8_t val; 130 uint16_t *p = (uint16_t *)d; 131 132 for(x = 0; x < width; x++) { 133 val = *s++; 134 *p++ = s1->palette[val]; 135 } 136 } 137 138 static void tcx_draw_line8(TCXState *s1, uint8_t *d, 139 const uint8_t *s, int width) 140 { 141 int x; 142 uint8_t val; 143 144 for(x = 0; x < width; x++) { 145 val = *s++; 146 *d++ = s1->palette[val]; 147 } 148 } 149 150 /* 151 XXX Could be much more optimal: 152 * detect if line/page/whole screen is in 24 bit mode 153 * if destination is also BGR, use memcpy 154 */ 155 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 156 const uint8_t *s, int width, 157 const uint32_t *cplane, 158 const uint32_t *s24) 159 { 160 DisplaySurface *surface = qemu_console_surface(s1->con); 161 int x, bgr, r, g, b; 162 uint8_t val, *p8; 163 uint32_t *p = (uint32_t *)d; 164 uint32_t dval; 165 166 bgr = is_surface_bgr(surface); 167 for(x = 0; x < width; x++, s++, s24++) { 168 if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) { 169 // 24-bit direct, BGR order 170 p8 = (uint8_t *)s24; 171 p8++; 172 b = *p8++; 173 g = *p8++; 174 r = *p8; 175 if (bgr) 176 dval = rgb_to_pixel32bgr(r, g, b); 177 else 178 dval = rgb_to_pixel32(r, g, b); 179 } else { 180 val = *s; 181 dval = s1->palette[val]; 182 } 183 *p++ = dval; 184 } 185 } 186 187 static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24, 188 ram_addr_t cpage) 189 { 190 int ret; 191 192 ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, 193 DIRTY_MEMORY_VGA); 194 ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, 195 DIRTY_MEMORY_VGA); 196 ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, 197 DIRTY_MEMORY_VGA); 198 return ret; 199 } 200 201 static inline void reset_dirty(TCXState *ts, ram_addr_t page_min, 202 ram_addr_t page_max, ram_addr_t page24, 203 ram_addr_t cpage) 204 { 205 memory_region_reset_dirty(&ts->vram_mem, 206 page_min, 207 (page_max - page_min) + TARGET_PAGE_SIZE, 208 DIRTY_MEMORY_VGA); 209 memory_region_reset_dirty(&ts->vram_mem, 210 page24 + page_min * 4, 211 (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 212 DIRTY_MEMORY_VGA); 213 memory_region_reset_dirty(&ts->vram_mem, 214 cpage + page_min * 4, 215 (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 216 DIRTY_MEMORY_VGA); 217 } 218 219 /* Fixed line length 1024 allows us to do nice tricks not possible on 220 VGA... */ 221 static void tcx_update_display(void *opaque) 222 { 223 TCXState *ts = opaque; 224 DisplaySurface *surface = qemu_console_surface(ts->con); 225 ram_addr_t page, page_min, page_max; 226 int y, y_start, dd, ds; 227 uint8_t *d, *s; 228 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); 229 230 if (surface_bits_per_pixel(surface) == 0) { 231 return; 232 } 233 234 page = 0; 235 y_start = -1; 236 page_min = -1; 237 page_max = 0; 238 d = surface_data(surface); 239 s = ts->vram; 240 dd = surface_stride(surface); 241 ds = 1024; 242 243 switch (surface_bits_per_pixel(surface)) { 244 case 32: 245 f = tcx_draw_line32; 246 break; 247 case 15: 248 case 16: 249 f = tcx_draw_line16; 250 break; 251 default: 252 case 8: 253 f = tcx_draw_line8; 254 break; 255 case 0: 256 return; 257 } 258 259 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) { 260 if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, 261 DIRTY_MEMORY_VGA)) { 262 if (y_start < 0) 263 y_start = y; 264 if (page < page_min) 265 page_min = page; 266 if (page > page_max) 267 page_max = page; 268 f(ts, d, s, ts->width); 269 d += dd; 270 s += ds; 271 f(ts, d, s, ts->width); 272 d += dd; 273 s += ds; 274 f(ts, d, s, ts->width); 275 d += dd; 276 s += ds; 277 f(ts, d, s, ts->width); 278 d += dd; 279 s += ds; 280 } else { 281 if (y_start >= 0) { 282 /* flush to display */ 283 dpy_gfx_update(ts->con, 0, y_start, 284 ts->width, y - y_start); 285 y_start = -1; 286 } 287 d += dd * 4; 288 s += ds * 4; 289 } 290 } 291 if (y_start >= 0) { 292 /* flush to display */ 293 dpy_gfx_update(ts->con, 0, y_start, 294 ts->width, y - y_start); 295 } 296 /* reset modified pages */ 297 if (page_max >= page_min) { 298 memory_region_reset_dirty(&ts->vram_mem, 299 page_min, 300 (page_max - page_min) + TARGET_PAGE_SIZE, 301 DIRTY_MEMORY_VGA); 302 } 303 } 304 305 static void tcx24_update_display(void *opaque) 306 { 307 TCXState *ts = opaque; 308 DisplaySurface *surface = qemu_console_surface(ts->con); 309 ram_addr_t page, page_min, page_max, cpage, page24; 310 int y, y_start, dd, ds; 311 uint8_t *d, *s; 312 uint32_t *cptr, *s24; 313 314 if (surface_bits_per_pixel(surface) != 32) { 315 return; 316 } 317 318 page = 0; 319 page24 = ts->vram24_offset; 320 cpage = ts->cplane_offset; 321 y_start = -1; 322 page_min = -1; 323 page_max = 0; 324 d = surface_data(surface); 325 s = ts->vram; 326 s24 = ts->vram24; 327 cptr = ts->cplane; 328 dd = surface_stride(surface); 329 ds = 1024; 330 331 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE, 332 page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { 333 if (check_dirty(ts, page, page24, cpage)) { 334 if (y_start < 0) 335 y_start = y; 336 if (page < page_min) 337 page_min = page; 338 if (page > page_max) 339 page_max = page; 340 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 341 d += dd; 342 s += ds; 343 cptr += ds; 344 s24 += ds; 345 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 346 d += dd; 347 s += ds; 348 cptr += ds; 349 s24 += ds; 350 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 351 d += dd; 352 s += ds; 353 cptr += ds; 354 s24 += ds; 355 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 356 d += dd; 357 s += ds; 358 cptr += ds; 359 s24 += ds; 360 } else { 361 if (y_start >= 0) { 362 /* flush to display */ 363 dpy_gfx_update(ts->con, 0, y_start, 364 ts->width, y - y_start); 365 y_start = -1; 366 } 367 d += dd * 4; 368 s += ds * 4; 369 cptr += ds * 4; 370 s24 += ds * 4; 371 } 372 } 373 if (y_start >= 0) { 374 /* flush to display */ 375 dpy_gfx_update(ts->con, 0, y_start, 376 ts->width, y - y_start); 377 } 378 /* reset modified pages */ 379 if (page_max >= page_min) { 380 reset_dirty(ts, page_min, page_max, page24, cpage); 381 } 382 } 383 384 static void tcx_invalidate_display(void *opaque) 385 { 386 TCXState *s = opaque; 387 388 tcx_set_dirty(s); 389 qemu_console_resize(s->con, s->width, s->height); 390 } 391 392 static void tcx24_invalidate_display(void *opaque) 393 { 394 TCXState *s = opaque; 395 396 tcx_set_dirty(s); 397 tcx24_set_dirty(s); 398 qemu_console_resize(s->con, s->width, s->height); 399 } 400 401 static int vmstate_tcx_post_load(void *opaque, int version_id) 402 { 403 TCXState *s = opaque; 404 405 update_palette_entries(s, 0, 256); 406 if (s->depth == 24) { 407 tcx24_set_dirty(s); 408 } else { 409 tcx_set_dirty(s); 410 } 411 412 return 0; 413 } 414 415 static const VMStateDescription vmstate_tcx = { 416 .name ="tcx", 417 .version_id = 4, 418 .minimum_version_id = 4, 419 .post_load = vmstate_tcx_post_load, 420 .fields = (VMStateField[]) { 421 VMSTATE_UINT16(height, TCXState), 422 VMSTATE_UINT16(width, TCXState), 423 VMSTATE_UINT16(depth, TCXState), 424 VMSTATE_BUFFER(r, TCXState), 425 VMSTATE_BUFFER(g, TCXState), 426 VMSTATE_BUFFER(b, TCXState), 427 VMSTATE_UINT8(dac_index, TCXState), 428 VMSTATE_UINT8(dac_state, TCXState), 429 VMSTATE_END_OF_LIST() 430 } 431 }; 432 433 static void tcx_reset(DeviceState *d) 434 { 435 TCXState *s = TCX(d); 436 437 /* Initialize palette */ 438 memset(s->r, 0, 256); 439 memset(s->g, 0, 256); 440 memset(s->b, 0, 256); 441 s->r[255] = s->g[255] = s->b[255] = 255; 442 update_palette_entries(s, 0, 256); 443 memset(s->vram, 0, MAXX*MAXY); 444 memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 445 DIRTY_MEMORY_VGA); 446 s->dac_index = 0; 447 s->dac_state = 0; 448 } 449 450 static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 451 unsigned size) 452 { 453 return 0; 454 } 455 456 static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 457 unsigned size) 458 { 459 TCXState *s = opaque; 460 461 switch (addr) { 462 case 0: 463 s->dac_index = val >> 24; 464 s->dac_state = 0; 465 break; 466 case 4: 467 switch (s->dac_state) { 468 case 0: 469 s->r[s->dac_index] = val >> 24; 470 update_palette_entries(s, s->dac_index, s->dac_index + 1); 471 s->dac_state++; 472 break; 473 case 1: 474 s->g[s->dac_index] = val >> 24; 475 update_palette_entries(s, s->dac_index, s->dac_index + 1); 476 s->dac_state++; 477 break; 478 case 2: 479 s->b[s->dac_index] = val >> 24; 480 update_palette_entries(s, s->dac_index, s->dac_index + 1); 481 s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement 482 default: 483 s->dac_state = 0; 484 break; 485 } 486 break; 487 default: 488 break; 489 } 490 } 491 492 static const MemoryRegionOps tcx_dac_ops = { 493 .read = tcx_dac_readl, 494 .write = tcx_dac_writel, 495 .endianness = DEVICE_NATIVE_ENDIAN, 496 .valid = { 497 .min_access_size = 4, 498 .max_access_size = 4, 499 }, 500 }; 501 502 static uint64_t dummy_readl(void *opaque, hwaddr addr, 503 unsigned size) 504 { 505 return 0; 506 } 507 508 static void dummy_writel(void *opaque, hwaddr addr, 509 uint64_t val, unsigned size) 510 { 511 } 512 513 static const MemoryRegionOps dummy_ops = { 514 .read = dummy_readl, 515 .write = dummy_writel, 516 .endianness = DEVICE_NATIVE_ENDIAN, 517 .valid = { 518 .min_access_size = 4, 519 .max_access_size = 4, 520 }, 521 }; 522 523 static const GraphicHwOps tcx_ops = { 524 .invalidate = tcx_invalidate_display, 525 .gfx_update = tcx_update_display, 526 }; 527 528 static const GraphicHwOps tcx24_ops = { 529 .invalidate = tcx24_invalidate_display, 530 .gfx_update = tcx24_update_display, 531 }; 532 533 static void tcx_initfn(Object *obj) 534 { 535 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 536 TCXState *s = TCX(obj); 537 538 memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE); 539 memory_region_set_readonly(&s->rom, true); 540 sysbus_init_mmio(sbd, &s->rom); 541 542 /* DAC */ 543 memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s, 544 "tcx.dac", TCX_DAC_NREGS); 545 sysbus_init_mmio(sbd, &s->dac); 546 547 /* TEC (dummy) */ 548 memory_region_init_io(&s->tec, OBJECT(s), &dummy_ops, s, 549 "tcx.tec", TCX_TEC_NREGS); 550 sysbus_init_mmio(sbd, &s->tec); 551 552 /* THC: NetBSD writes here even with 8-bit display: dummy */ 553 memory_region_init_io(&s->thc24, OBJECT(s), &dummy_ops, s, "tcx.thc24", 554 TCX_THC_NREGS_24); 555 sysbus_init_mmio(sbd, &s->thc24); 556 557 return; 558 } 559 560 static void tcx_realizefn(DeviceState *dev, Error **errp) 561 { 562 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 563 TCXState *s = TCX(dev); 564 ram_addr_t vram_offset = 0; 565 int size, ret; 566 uint8_t *vram_base; 567 char *fcode_filename; 568 569 memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 570 s->vram_size * (1 + 4 + 4)); 571 vmstate_register_ram_global(&s->vram_mem); 572 vram_base = memory_region_get_ram_ptr(&s->vram_mem); 573 574 /* FCode ROM */ 575 vmstate_register_ram_global(&s->rom); 576 fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 577 if (fcode_filename) { 578 ret = load_image_targphys(fcode_filename, s->prom_addr, 579 FCODE_MAX_ROM_SIZE); 580 if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 581 error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); 582 } 583 } 584 585 /* 8-bit plane */ 586 s->vram = vram_base; 587 size = s->vram_size; 588 memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 589 &s->vram_mem, vram_offset, size); 590 sysbus_init_mmio(sbd, &s->vram_8bit); 591 vram_offset += size; 592 vram_base += size; 593 594 if (s->depth == 24) { 595 /* 24-bit plane */ 596 size = s->vram_size * 4; 597 s->vram24 = (uint32_t *)vram_base; 598 s->vram24_offset = vram_offset; 599 memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 600 &s->vram_mem, vram_offset, size); 601 sysbus_init_mmio(sbd, &s->vram_24bit); 602 vram_offset += size; 603 vram_base += size; 604 605 /* Control plane */ 606 size = s->vram_size * 4; 607 s->cplane = (uint32_t *)vram_base; 608 s->cplane_offset = vram_offset; 609 memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 610 &s->vram_mem, vram_offset, size); 611 sysbus_init_mmio(sbd, &s->vram_cplane); 612 613 s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); 614 } else { 615 /* THC 8 bit (dummy) */ 616 memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", 617 TCX_THC_NREGS_8); 618 sysbus_init_mmio(sbd, &s->thc8); 619 620 s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); 621 } 622 623 qemu_console_resize(s->con, s->width, s->height); 624 } 625 626 static Property tcx_properties[] = { 627 DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 628 DEFINE_PROP_UINT16("width", TCXState, width, -1), 629 DEFINE_PROP_UINT16("height", TCXState, height, -1), 630 DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 631 DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), 632 DEFINE_PROP_END_OF_LIST(), 633 }; 634 635 static void tcx_class_init(ObjectClass *klass, void *data) 636 { 637 DeviceClass *dc = DEVICE_CLASS(klass); 638 639 dc->realize = tcx_realizefn; 640 dc->reset = tcx_reset; 641 dc->vmsd = &vmstate_tcx; 642 dc->props = tcx_properties; 643 } 644 645 static const TypeInfo tcx_info = { 646 .name = TYPE_TCX, 647 .parent = TYPE_SYS_BUS_DEVICE, 648 .instance_size = sizeof(TCXState), 649 .instance_init = tcx_initfn, 650 .class_init = tcx_class_init, 651 }; 652 653 static void tcx_register_types(void) 654 { 655 type_register_static(&tcx_info); 656 } 657 658 type_init(tcx_register_types) 659