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