1 /* 2 * Arm PrimeCell PL110 Color LCD Controller 3 * 4 * Copyright (c) 2005-2009 CodeSourcery. 5 * Written by Paul Brook 6 * 7 * This code is licensed under the GNU LGPL 8 */ 9 10 #include "qemu/osdep.h" 11 #include "hw/irq.h" 12 #include "hw/sysbus.h" 13 #include "migration/vmstate.h" 14 #include "ui/console.h" 15 #include "framebuffer.h" 16 #include "ui/pixel_ops.h" 17 #include "qemu/timer.h" 18 #include "qemu/log.h" 19 #include "qemu/module.h" 20 #include "qom/object.h" 21 22 #define PL110_CR_EN 0x001 23 #define PL110_CR_BGR 0x100 24 #define PL110_CR_BEBO 0x200 25 #define PL110_CR_BEPO 0x400 26 #define PL110_CR_PWR 0x800 27 #define PL110_IE_NB 0x004 28 #define PL110_IE_VC 0x008 29 30 enum pl110_bppmode 31 { 32 BPP_1, 33 BPP_2, 34 BPP_4, 35 BPP_8, 36 BPP_16, 37 BPP_32, 38 BPP_16_565, /* PL111 only */ 39 BPP_12 /* PL111 only */ 40 }; 41 42 43 /* The Versatile/PB uses a slightly modified PL110 controller. */ 44 enum pl110_version 45 { 46 VERSION_PL110, 47 VERSION_PL110_VERSATILE, 48 VERSION_PL111 49 }; 50 51 #define TYPE_PL110 "pl110" 52 OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110) 53 54 struct PL110State { 55 SysBusDevice parent_obj; 56 57 MemoryRegion iomem; 58 MemoryRegionSection fbsection; 59 QemuConsole *con; 60 QEMUTimer *vblank_timer; 61 62 int version; 63 uint32_t timing[4]; 64 uint32_t cr; 65 uint32_t upbase; 66 uint32_t lpbase; 67 uint32_t int_status; 68 uint32_t int_mask; 69 int cols; 70 int rows; 71 enum pl110_bppmode bpp; 72 int invalidate; 73 uint32_t mux_ctrl; 74 uint32_t palette[256]; 75 uint32_t raw_palette[128]; 76 qemu_irq irq; 77 }; 78 79 static int vmstate_pl110_post_load(void *opaque, int version_id); 80 81 static const VMStateDescription vmstate_pl110 = { 82 .name = "pl110", 83 .version_id = 2, 84 .minimum_version_id = 1, 85 .post_load = vmstate_pl110_post_load, 86 .fields = (VMStateField[]) { 87 VMSTATE_INT32(version, PL110State), 88 VMSTATE_UINT32_ARRAY(timing, PL110State, 4), 89 VMSTATE_UINT32(cr, PL110State), 90 VMSTATE_UINT32(upbase, PL110State), 91 VMSTATE_UINT32(lpbase, PL110State), 92 VMSTATE_UINT32(int_status, PL110State), 93 VMSTATE_UINT32(int_mask, PL110State), 94 VMSTATE_INT32(cols, PL110State), 95 VMSTATE_INT32(rows, PL110State), 96 VMSTATE_UINT32(bpp, PL110State), 97 VMSTATE_INT32(invalidate, PL110State), 98 VMSTATE_UINT32_ARRAY(palette, PL110State, 256), 99 VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128), 100 VMSTATE_UINT32_V(mux_ctrl, PL110State, 2), 101 VMSTATE_END_OF_LIST() 102 } 103 }; 104 105 static const unsigned char pl110_id[] = 106 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 107 108 static const unsigned char pl111_id[] = { 109 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 110 }; 111 112 113 /* Indexed by pl110_version */ 114 static const unsigned char *idregs[] = { 115 pl110_id, 116 /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board 117 * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware 118 * itself has the same ID values as a stock PL110, and guests (in 119 * particular Linux) rely on this. We emulate what the hardware does, 120 * rather than what the docs claim it ought to do. 121 */ 122 pl110_id, 123 pl111_id 124 }; 125 126 #define BITS 32 127 #define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0) 128 129 #undef RGB 130 #define BORDER bgr 131 #define ORDER 0 132 #include "pl110_template.h" 133 #define ORDER 1 134 #include "pl110_template.h" 135 #define ORDER 2 136 #include "pl110_template.h" 137 #undef BORDER 138 #define RGB 139 #define BORDER rgb 140 #define ORDER 0 141 #include "pl110_template.h" 142 #define ORDER 1 143 #include "pl110_template.h" 144 #define ORDER 2 145 #include "pl110_template.h" 146 #undef BORDER 147 148 static drawfn pl110_draw_fn_32[48] = { 149 pl110_draw_line1_lblp_bgr32, 150 pl110_draw_line2_lblp_bgr32, 151 pl110_draw_line4_lblp_bgr32, 152 pl110_draw_line8_lblp_bgr32, 153 pl110_draw_line16_555_lblp_bgr32, 154 pl110_draw_line32_lblp_bgr32, 155 pl110_draw_line16_lblp_bgr32, 156 pl110_draw_line12_lblp_bgr32, 157 158 pl110_draw_line1_bbbp_bgr32, 159 pl110_draw_line2_bbbp_bgr32, 160 pl110_draw_line4_bbbp_bgr32, 161 pl110_draw_line8_bbbp_bgr32, 162 pl110_draw_line16_555_bbbp_bgr32, 163 pl110_draw_line32_bbbp_bgr32, 164 pl110_draw_line16_bbbp_bgr32, 165 pl110_draw_line12_bbbp_bgr32, 166 167 pl110_draw_line1_lbbp_bgr32, 168 pl110_draw_line2_lbbp_bgr32, 169 pl110_draw_line4_lbbp_bgr32, 170 pl110_draw_line8_lbbp_bgr32, 171 pl110_draw_line16_555_lbbp_bgr32, 172 pl110_draw_line32_lbbp_bgr32, 173 pl110_draw_line16_lbbp_bgr32, 174 pl110_draw_line12_lbbp_bgr32, 175 176 pl110_draw_line1_lblp_rgb32, 177 pl110_draw_line2_lblp_rgb32, 178 pl110_draw_line4_lblp_rgb32, 179 pl110_draw_line8_lblp_rgb32, 180 pl110_draw_line16_555_lblp_rgb32, 181 pl110_draw_line32_lblp_rgb32, 182 pl110_draw_line16_lblp_rgb32, 183 pl110_draw_line12_lblp_rgb32, 184 185 pl110_draw_line1_bbbp_rgb32, 186 pl110_draw_line2_bbbp_rgb32, 187 pl110_draw_line4_bbbp_rgb32, 188 pl110_draw_line8_bbbp_rgb32, 189 pl110_draw_line16_555_bbbp_rgb32, 190 pl110_draw_line32_bbbp_rgb32, 191 pl110_draw_line16_bbbp_rgb32, 192 pl110_draw_line12_bbbp_rgb32, 193 194 pl110_draw_line1_lbbp_rgb32, 195 pl110_draw_line2_lbbp_rgb32, 196 pl110_draw_line4_lbbp_rgb32, 197 pl110_draw_line8_lbbp_rgb32, 198 pl110_draw_line16_555_lbbp_rgb32, 199 pl110_draw_line32_lbbp_rgb32, 200 pl110_draw_line16_lbbp_rgb32, 201 pl110_draw_line12_lbbp_rgb32, 202 }; 203 204 #undef BITS 205 #undef COPY_PIXEL 206 207 208 static int pl110_enabled(PL110State *s) 209 { 210 return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); 211 } 212 213 static void pl110_update_display(void *opaque) 214 { 215 PL110State *s = (PL110State *)opaque; 216 SysBusDevice *sbd; 217 DisplaySurface *surface = qemu_console_surface(s->con); 218 drawfn fn; 219 int src_width; 220 int bpp_offset; 221 int first; 222 int last; 223 224 if (!pl110_enabled(s)) { 225 return; 226 } 227 228 sbd = SYS_BUS_DEVICE(s); 229 230 if (s->cr & PL110_CR_BGR) 231 bpp_offset = 0; 232 else 233 bpp_offset = 24; 234 235 if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) { 236 /* The PL110's native 16 bit mode is 5551; however 237 * most boards with a PL110 implement an external 238 * mux which allows bits to be reshuffled to give 239 * 565 format. The mux is typically controlled by 240 * an external system register. 241 * This is controlled by a GPIO input pin 242 * so boards can wire it up to their register. 243 * 244 * The PL111 straightforwardly implements both 245 * 5551 and 565 under control of the bpp field 246 * in the LCDControl register. 247 */ 248 switch (s->mux_ctrl) { 249 case 3: /* 565 BGR */ 250 bpp_offset = (BPP_16_565 - BPP_16); 251 break; 252 case 1: /* 5551 */ 253 break; 254 case 0: /* 888; also if we have loaded vmstate from an old version */ 255 case 2: /* 565 RGB */ 256 default: 257 /* treat as 565 but honour BGR bit */ 258 bpp_offset += (BPP_16_565 - BPP_16); 259 break; 260 } 261 } 262 263 if (s->cr & PL110_CR_BEBO) { 264 fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset]; 265 } else if (s->cr & PL110_CR_BEPO) { 266 fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset]; 267 } else { 268 fn = pl110_draw_fn_32[s->bpp + bpp_offset]; 269 } 270 271 src_width = s->cols; 272 switch (s->bpp) { 273 case BPP_1: 274 src_width >>= 3; 275 break; 276 case BPP_2: 277 src_width >>= 2; 278 break; 279 case BPP_4: 280 src_width >>= 1; 281 break; 282 case BPP_8: 283 break; 284 case BPP_16: 285 case BPP_16_565: 286 case BPP_12: 287 src_width <<= 1; 288 break; 289 case BPP_32: 290 src_width <<= 2; 291 break; 292 } 293 first = 0; 294 if (s->invalidate) { 295 framebuffer_update_memory_section(&s->fbsection, 296 sysbus_address_space(sbd), 297 s->upbase, 298 s->rows, src_width); 299 } 300 301 framebuffer_update_display(surface, &s->fbsection, 302 s->cols, s->rows, 303 src_width, s->cols * 4, 0, 304 s->invalidate, 305 fn, s->palette, 306 &first, &last); 307 308 if (first >= 0) { 309 dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); 310 } 311 s->invalidate = 0; 312 } 313 314 static void pl110_invalidate_display(void * opaque) 315 { 316 PL110State *s = (PL110State *)opaque; 317 s->invalidate = 1; 318 if (pl110_enabled(s)) { 319 qemu_console_resize(s->con, s->cols, s->rows); 320 } 321 } 322 323 static void pl110_update_palette(PL110State *s, int n) 324 { 325 DisplaySurface *surface = qemu_console_surface(s->con); 326 int i; 327 uint32_t raw; 328 unsigned int r, g, b; 329 330 raw = s->raw_palette[n]; 331 n <<= 1; 332 for (i = 0; i < 2; i++) { 333 r = (raw & 0x1f) << 3; 334 raw >>= 5; 335 g = (raw & 0x1f) << 3; 336 raw >>= 5; 337 b = (raw & 0x1f) << 3; 338 /* The I bit is ignored. */ 339 raw >>= 6; 340 switch (surface_bits_per_pixel(surface)) { 341 case 8: 342 s->palette[n] = rgb_to_pixel8(r, g, b); 343 break; 344 case 15: 345 s->palette[n] = rgb_to_pixel15(r, g, b); 346 break; 347 case 16: 348 s->palette[n] = rgb_to_pixel16(r, g, b); 349 break; 350 case 24: 351 case 32: 352 s->palette[n] = rgb_to_pixel32(r, g, b); 353 break; 354 } 355 n++; 356 } 357 } 358 359 static void pl110_resize(PL110State *s, int width, int height) 360 { 361 if (width != s->cols || height != s->rows) { 362 if (pl110_enabled(s)) { 363 qemu_console_resize(s->con, width, height); 364 } 365 } 366 s->cols = width; 367 s->rows = height; 368 } 369 370 /* Update interrupts. */ 371 static void pl110_update(PL110State *s) 372 { 373 /* Raise IRQ if enabled and any status bit is 1 */ 374 if (s->int_status & s->int_mask) { 375 qemu_irq_raise(s->irq); 376 } else { 377 qemu_irq_lower(s->irq); 378 } 379 } 380 381 static void pl110_vblank_interrupt(void *opaque) 382 { 383 PL110State *s = opaque; 384 385 /* Fire the vertical compare and next base IRQs and re-arm */ 386 s->int_status |= (PL110_IE_NB | PL110_IE_VC); 387 timer_mod(s->vblank_timer, 388 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 389 NANOSECONDS_PER_SECOND / 60); 390 pl110_update(s); 391 } 392 393 static uint64_t pl110_read(void *opaque, hwaddr offset, 394 unsigned size) 395 { 396 PL110State *s = (PL110State *)opaque; 397 398 if (offset >= 0xfe0 && offset < 0x1000) { 399 return idregs[s->version][(offset - 0xfe0) >> 2]; 400 } 401 if (offset >= 0x200 && offset < 0x400) { 402 return s->raw_palette[(offset - 0x200) >> 2]; 403 } 404 switch (offset >> 2) { 405 case 0: /* LCDTiming0 */ 406 return s->timing[0]; 407 case 1: /* LCDTiming1 */ 408 return s->timing[1]; 409 case 2: /* LCDTiming2 */ 410 return s->timing[2]; 411 case 3: /* LCDTiming3 */ 412 return s->timing[3]; 413 case 4: /* LCDUPBASE */ 414 return s->upbase; 415 case 5: /* LCDLPBASE */ 416 return s->lpbase; 417 case 6: /* LCDIMSC */ 418 if (s->version != VERSION_PL110) { 419 return s->cr; 420 } 421 return s->int_mask; 422 case 7: /* LCDControl */ 423 if (s->version != VERSION_PL110) { 424 return s->int_mask; 425 } 426 return s->cr; 427 case 8: /* LCDRIS */ 428 return s->int_status; 429 case 9: /* LCDMIS */ 430 return s->int_status & s->int_mask; 431 case 11: /* LCDUPCURR */ 432 /* TODO: Implement vertical refresh. */ 433 return s->upbase; 434 case 12: /* LCDLPCURR */ 435 return s->lpbase; 436 default: 437 qemu_log_mask(LOG_GUEST_ERROR, 438 "pl110_read: Bad offset %x\n", (int)offset); 439 return 0; 440 } 441 } 442 443 static void pl110_write(void *opaque, hwaddr offset, 444 uint64_t val, unsigned size) 445 { 446 PL110State *s = (PL110State *)opaque; 447 int n; 448 449 /* For simplicity invalidate the display whenever a control register 450 is written to. */ 451 s->invalidate = 1; 452 if (offset >= 0x200 && offset < 0x400) { 453 /* Palette. */ 454 n = (offset - 0x200) >> 2; 455 s->raw_palette[(offset - 0x200) >> 2] = val; 456 pl110_update_palette(s, n); 457 return; 458 } 459 switch (offset >> 2) { 460 case 0: /* LCDTiming0 */ 461 s->timing[0] = val; 462 n = ((val & 0xfc) + 4) * 4; 463 pl110_resize(s, n, s->rows); 464 break; 465 case 1: /* LCDTiming1 */ 466 s->timing[1] = val; 467 n = (val & 0x3ff) + 1; 468 pl110_resize(s, s->cols, n); 469 break; 470 case 2: /* LCDTiming2 */ 471 s->timing[2] = val; 472 break; 473 case 3: /* LCDTiming3 */ 474 s->timing[3] = val; 475 break; 476 case 4: /* LCDUPBASE */ 477 s->upbase = val; 478 break; 479 case 5: /* LCDLPBASE */ 480 s->lpbase = val; 481 break; 482 case 6: /* LCDIMSC */ 483 if (s->version != VERSION_PL110) { 484 goto control; 485 } 486 imsc: 487 s->int_mask = val; 488 pl110_update(s); 489 break; 490 case 7: /* LCDControl */ 491 if (s->version != VERSION_PL110) { 492 goto imsc; 493 } 494 control: 495 s->cr = val; 496 s->bpp = (val >> 1) & 7; 497 if (pl110_enabled(s)) { 498 qemu_console_resize(s->con, s->cols, s->rows); 499 timer_mod(s->vblank_timer, 500 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 501 NANOSECONDS_PER_SECOND / 60); 502 } else { 503 timer_del(s->vblank_timer); 504 } 505 break; 506 case 10: /* LCDICR */ 507 s->int_status &= ~val; 508 pl110_update(s); 509 break; 510 default: 511 qemu_log_mask(LOG_GUEST_ERROR, 512 "pl110_write: Bad offset %x\n", (int)offset); 513 } 514 } 515 516 static const MemoryRegionOps pl110_ops = { 517 .read = pl110_read, 518 .write = pl110_write, 519 .endianness = DEVICE_NATIVE_ENDIAN, 520 }; 521 522 static void pl110_mux_ctrl_set(void *opaque, int line, int level) 523 { 524 PL110State *s = (PL110State *)opaque; 525 s->mux_ctrl = level; 526 } 527 528 static int vmstate_pl110_post_load(void *opaque, int version_id) 529 { 530 PL110State *s = opaque; 531 /* Make sure we redraw, and at the right size */ 532 pl110_invalidate_display(s); 533 return 0; 534 } 535 536 static const GraphicHwOps pl110_gfx_ops = { 537 .invalidate = pl110_invalidate_display, 538 .gfx_update = pl110_update_display, 539 }; 540 541 static void pl110_realize(DeviceState *dev, Error **errp) 542 { 543 PL110State *s = PL110(dev); 544 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 545 546 memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); 547 sysbus_init_mmio(sbd, &s->iomem); 548 sysbus_init_irq(sbd, &s->irq); 549 s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 550 pl110_vblank_interrupt, s); 551 qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); 552 s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); 553 } 554 555 static void pl110_init(Object *obj) 556 { 557 PL110State *s = PL110(obj); 558 559 s->version = VERSION_PL110; 560 } 561 562 static void pl110_versatile_init(Object *obj) 563 { 564 PL110State *s = PL110(obj); 565 566 s->version = VERSION_PL110_VERSATILE; 567 } 568 569 static void pl111_init(Object *obj) 570 { 571 PL110State *s = PL110(obj); 572 573 s->version = VERSION_PL111; 574 } 575 576 static void pl110_class_init(ObjectClass *klass, void *data) 577 { 578 DeviceClass *dc = DEVICE_CLASS(klass); 579 580 set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 581 dc->vmsd = &vmstate_pl110; 582 dc->realize = pl110_realize; 583 } 584 585 static const TypeInfo pl110_info = { 586 .name = TYPE_PL110, 587 .parent = TYPE_SYS_BUS_DEVICE, 588 .instance_size = sizeof(PL110State), 589 .instance_init = pl110_init, 590 .class_init = pl110_class_init, 591 }; 592 593 static const TypeInfo pl110_versatile_info = { 594 .name = "pl110_versatile", 595 .parent = TYPE_PL110, 596 .instance_init = pl110_versatile_init, 597 }; 598 599 static const TypeInfo pl111_info = { 600 .name = "pl111", 601 .parent = TYPE_PL110, 602 .instance_init = pl111_init, 603 }; 604 605 static void pl110_register_types(void) 606 { 607 type_register_static(&pl110_info); 608 type_register_static(&pl110_versatile_info); 609 type_register_static(&pl111_info); 610 } 611 612 type_init(pl110_register_types) 613