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 .minimum_version_id_old = 4, 420 .post_load = vmstate_tcx_post_load, 421 .fields = (VMStateField []) { 422 VMSTATE_UINT16(height, TCXState), 423 VMSTATE_UINT16(width, TCXState), 424 VMSTATE_UINT16(depth, TCXState), 425 VMSTATE_BUFFER(r, TCXState), 426 VMSTATE_BUFFER(g, TCXState), 427 VMSTATE_BUFFER(b, TCXState), 428 VMSTATE_UINT8(dac_index, TCXState), 429 VMSTATE_UINT8(dac_state, TCXState), 430 VMSTATE_END_OF_LIST() 431 } 432 }; 433 434 static void tcx_reset(DeviceState *d) 435 { 436 TCXState *s = TCX(d); 437 438 /* Initialize palette */ 439 memset(s->r, 0, 256); 440 memset(s->g, 0, 256); 441 memset(s->b, 0, 256); 442 s->r[255] = s->g[255] = s->b[255] = 255; 443 update_palette_entries(s, 0, 256); 444 memset(s->vram, 0, MAXX*MAXY); 445 memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 446 DIRTY_MEMORY_VGA); 447 s->dac_index = 0; 448 s->dac_state = 0; 449 } 450 451 static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 452 unsigned size) 453 { 454 return 0; 455 } 456 457 static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 458 unsigned size) 459 { 460 TCXState *s = opaque; 461 462 switch (addr) { 463 case 0: 464 s->dac_index = val >> 24; 465 s->dac_state = 0; 466 break; 467 case 4: 468 switch (s->dac_state) { 469 case 0: 470 s->r[s->dac_index] = val >> 24; 471 update_palette_entries(s, s->dac_index, s->dac_index + 1); 472 s->dac_state++; 473 break; 474 case 1: 475 s->g[s->dac_index] = val >> 24; 476 update_palette_entries(s, s->dac_index, s->dac_index + 1); 477 s->dac_state++; 478 break; 479 case 2: 480 s->b[s->dac_index] = val >> 24; 481 update_palette_entries(s, s->dac_index, s->dac_index + 1); 482 s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement 483 default: 484 s->dac_state = 0; 485 break; 486 } 487 break; 488 default: 489 break; 490 } 491 } 492 493 static const MemoryRegionOps tcx_dac_ops = { 494 .read = tcx_dac_readl, 495 .write = tcx_dac_writel, 496 .endianness = DEVICE_NATIVE_ENDIAN, 497 .valid = { 498 .min_access_size = 4, 499 .max_access_size = 4, 500 }, 501 }; 502 503 static uint64_t dummy_readl(void *opaque, hwaddr addr, 504 unsigned size) 505 { 506 return 0; 507 } 508 509 static void dummy_writel(void *opaque, hwaddr addr, 510 uint64_t val, unsigned size) 511 { 512 } 513 514 static const MemoryRegionOps dummy_ops = { 515 .read = dummy_readl, 516 .write = dummy_writel, 517 .endianness = DEVICE_NATIVE_ENDIAN, 518 .valid = { 519 .min_access_size = 4, 520 .max_access_size = 4, 521 }, 522 }; 523 524 static const GraphicHwOps tcx_ops = { 525 .invalidate = tcx_invalidate_display, 526 .gfx_update = tcx_update_display, 527 }; 528 529 static const GraphicHwOps tcx24_ops = { 530 .invalidate = tcx24_invalidate_display, 531 .gfx_update = tcx24_update_display, 532 }; 533 534 static int tcx_init1(SysBusDevice *dev) 535 { 536 TCXState *s = TCX(dev); 537 ram_addr_t vram_offset = 0; 538 int size, ret; 539 uint8_t *vram_base; 540 char *fcode_filename; 541 542 memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 543 s->vram_size * (1 + 4 + 4)); 544 vmstate_register_ram_global(&s->vram_mem); 545 vram_base = memory_region_get_ram_ptr(&s->vram_mem); 546 547 /* FCode ROM */ 548 memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE); 549 vmstate_register_ram_global(&s->rom); 550 memory_region_set_readonly(&s->rom, true); 551 sysbus_init_mmio(dev, &s->rom); 552 553 fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 554 if (fcode_filename) { 555 ret = load_image_targphys(fcode_filename, s->prom_addr, 556 FCODE_MAX_ROM_SIZE); 557 if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 558 fprintf(stderr, "tcx: could not load prom '%s'\n", TCX_ROM_FILE); 559 return -1; 560 } 561 } 562 563 /* 8-bit plane */ 564 s->vram = vram_base; 565 size = s->vram_size; 566 memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 567 &s->vram_mem, vram_offset, size); 568 sysbus_init_mmio(dev, &s->vram_8bit); 569 vram_offset += size; 570 vram_base += size; 571 572 /* DAC */ 573 memory_region_init_io(&s->dac, OBJECT(s), &tcx_dac_ops, s, 574 "tcx.dac", TCX_DAC_NREGS); 575 sysbus_init_mmio(dev, &s->dac); 576 577 /* TEC (dummy) */ 578 memory_region_init_io(&s->tec, OBJECT(s), &dummy_ops, s, 579 "tcx.tec", TCX_TEC_NREGS); 580 sysbus_init_mmio(dev, &s->tec); 581 /* THC: NetBSD writes here even with 8-bit display: dummy */ 582 memory_region_init_io(&s->thc24, OBJECT(s), &dummy_ops, s, "tcx.thc24", 583 TCX_THC_NREGS_24); 584 sysbus_init_mmio(dev, &s->thc24); 585 586 if (s->depth == 24) { 587 /* 24-bit plane */ 588 size = s->vram_size * 4; 589 s->vram24 = (uint32_t *)vram_base; 590 s->vram24_offset = vram_offset; 591 memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 592 &s->vram_mem, vram_offset, size); 593 sysbus_init_mmio(dev, &s->vram_24bit); 594 vram_offset += size; 595 vram_base += size; 596 597 /* Control plane */ 598 size = s->vram_size * 4; 599 s->cplane = (uint32_t *)vram_base; 600 s->cplane_offset = vram_offset; 601 memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 602 &s->vram_mem, vram_offset, size); 603 sysbus_init_mmio(dev, &s->vram_cplane); 604 605 s->con = graphic_console_init(DEVICE(dev), &tcx24_ops, s); 606 } else { 607 /* THC 8 bit (dummy) */ 608 memory_region_init_io(&s->thc8, OBJECT(s), &dummy_ops, s, "tcx.thc8", 609 TCX_THC_NREGS_8); 610 sysbus_init_mmio(dev, &s->thc8); 611 612 s->con = graphic_console_init(DEVICE(dev), &tcx_ops, s); 613 } 614 615 qemu_console_resize(s->con, s->width, s->height); 616 return 0; 617 } 618 619 static Property tcx_properties[] = { 620 DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1), 621 DEFINE_PROP_UINT16("width", TCXState, width, -1), 622 DEFINE_PROP_UINT16("height", TCXState, height, -1), 623 DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 624 DEFINE_PROP_HEX64("prom_addr", TCXState, prom_addr, -1), 625 DEFINE_PROP_END_OF_LIST(), 626 }; 627 628 static void tcx_class_init(ObjectClass *klass, void *data) 629 { 630 DeviceClass *dc = DEVICE_CLASS(klass); 631 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 632 633 k->init = tcx_init1; 634 dc->reset = tcx_reset; 635 dc->vmsd = &vmstate_tcx; 636 dc->props = tcx_properties; 637 } 638 639 static const TypeInfo tcx_info = { 640 .name = TYPE_TCX, 641 .parent = TYPE_SYS_BUS_DEVICE, 642 .instance_size = sizeof(TCXState), 643 .class_init = tcx_class_init, 644 }; 645 646 static void tcx_register_types(void) 647 { 648 type_register_static(&tcx_info); 649 } 650 651 type_init(tcx_register_types) 652