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 int tcx_init1(SysBusDevice *dev) 534 { 535 TCXState *s = TCX(dev); 536 ram_addr_t vram_offset = 0; 537 int size, ret; 538 uint8_t *vram_base; 539 char *fcode_filename; 540 541 memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 542 s->vram_size * (1 + 4 + 4)); 543 vmstate_register_ram_global(&s->vram_mem); 544 vram_base = memory_region_get_ram_ptr(&s->vram_mem); 545 546 /* FCode ROM */ 547 memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE); 548 vmstate_register_ram_global(&s->rom); 549 memory_region_set_readonly(&s->rom, true); 550 sysbus_init_mmio(dev, &s->rom); 551 552 fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 553 if (fcode_filename) { 554 ret = load_image_targphys(fcode_filename, s->prom_addr, 555 FCODE_MAX_ROM_SIZE); 556 if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 557 fprintf(stderr, "tcx: could not load prom '%s'\n", TCX_ROM_FILE); 558 return -1; 559 } 560 } 561 562 /* 8-bit plane */ 563 s->vram = vram_base; 564 size = s->vram_size; 565 memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 566 &s->vram_mem, vram_offset, size); 567 sysbus_init_mmio(dev, &s->vram_8bit); 568 vram_offset += size; 569 vram_base += size; 570 571 /* DAC */ 572 memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s, 573 "tcx.dac", TCX_DAC_NREGS); 574 sysbus_init_mmio(dev, &s->dac); 575 576 /* TEC (dummy) */ 577 memory_region_init_io(&s->tec, OBJECT(s), &dummy_ops, s, 578 "tcx.tec", TCX_TEC_NREGS); 579 sysbus_init_mmio(dev, &s->tec); 580 /* THC: NetBSD writes here even with 8-bit display: dummy */ 581 memory_region_init_io(&s->thc24, OBJECT(s), &dummy_ops, s, "tcx.thc24", 582 TCX_THC_NREGS_24); 583 sysbus_init_mmio(dev, &s->thc24); 584 585 if (s->depth == 24) { 586 /* 24-bit plane */ 587 size = s->vram_size * 4; 588 s->vram24 = (uint32_t *)vram_base; 589 s->vram24_offset = vram_offset; 590 memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 591 &s->vram_mem, vram_offset, size); 592 sysbus_init_mmio(dev, &s->vram_24bit); 593 vram_offset += size; 594 vram_base += size; 595 596 /* Control plane */ 597 size = s->vram_size * 4; 598 s->cplane = (uint32_t *)vram_base; 599 s->cplane_offset = vram_offset; 600 memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 601 &s->vram_mem, vram_offset, size); 602 sysbus_init_mmio(dev, &s->vram_cplane); 603 604 s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); 605 } else { 606 /* THC 8 bit (dummy) */ 607 memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", 608 TCX_THC_NREGS_8); 609 sysbus_init_mmio(dev, &s->thc8); 610 611 s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); 612 } 613 614 qemu_console_resize(s->con, s->width, s->height); 615 return 0; 616 } 617 618 static Property tcx_properties[] = { 619 DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 620 DEFINE_PROP_UINT16("width", TCXState, width, -1), 621 DEFINE_PROP_UINT16("height", TCXState, height, -1), 622 DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 623 DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), 624 DEFINE_PROP_END_OF_LIST(), 625 }; 626 627 static void tcx_class_init(ObjectClass *klass, void *data) 628 { 629 DeviceClass *dc = DEVICE_CLASS(klass); 630 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 631 632 k->init = tcx_init1; 633 dc->reset = tcx_reset; 634 dc->vmsd = &vmstate_tcx; 635 dc->props = tcx_properties; 636 } 637 638 static const TypeInfo tcx_info = { 639 .name = TYPE_TCX, 640 .parent = TYPE_SYS_BUS_DEVICE, 641 .instance_size = sizeof(TCXState), 642 .class_init = tcx_class_init, 643 }; 644 645 static void tcx_register_types(void) 646 { 647 type_register_static(&tcx_info); 648 } 649 650 type_init(tcx_register_types) 651