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