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