1bdd5003aSpbrook /* 2bdd5003aSpbrook * Arm PrimeCell PL110 Color LCD Controller 3bdd5003aSpbrook * 42e9bdce5SPaul Brook * Copyright (c) 2005-2009 CodeSourcery. 5bdd5003aSpbrook * Written by Paul Brook 6bdd5003aSpbrook * 78e31bf38SMatthew Fernandez * This code is licensed under the GNU LGPL 8bdd5003aSpbrook */ 9bdd5003aSpbrook 108ef94f0bSPeter Maydell #include "qemu/osdep.h" 1164552b6bSMarkus Armbruster #include "hw/irq.h" 1283c9f4caSPaolo Bonzini #include "hw/sysbus.h" 13*c2093660SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h" 14d6454270SMarkus Armbruster #include "migration/vmstate.h" 1528ecbaeeSPaolo Bonzini #include "ui/console.h" 1647b43a1fSPaolo Bonzini #include "framebuffer.h" 1728ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 1824da047aSLinus Walleij #include "qemu/timer.h" 1903dd024fSPaolo Bonzini #include "qemu/log.h" 200b8fa32fSMarkus Armbruster #include "qemu/module.h" 21*c2093660SPhilippe Mathieu-Daudé #include "qapi/error.h" 22db1015e9SEduardo Habkost #include "qom/object.h" 23bdd5003aSpbrook 24bdd5003aSpbrook #define PL110_CR_EN 0x001 25e9c05b42Sbalrog #define PL110_CR_BGR 0x100 26bdd5003aSpbrook #define PL110_CR_BEBO 0x200 27bdd5003aSpbrook #define PL110_CR_BEPO 0x400 28bdd5003aSpbrook #define PL110_CR_PWR 0x800 2924da047aSLinus Walleij #define PL110_IE_NB 0x004 3024da047aSLinus Walleij #define PL110_IE_VC 0x008 31bdd5003aSpbrook 32bdd5003aSpbrook enum pl110_bppmode 33bdd5003aSpbrook { 34bdd5003aSpbrook BPP_1, 35bdd5003aSpbrook BPP_2, 36bdd5003aSpbrook BPP_4, 37bdd5003aSpbrook BPP_8, 38bdd5003aSpbrook BPP_16, 394fbf5556SPeter Maydell BPP_32, 404fbf5556SPeter Maydell BPP_16_565, /* PL111 only */ 414fbf5556SPeter Maydell BPP_12 /* PL111 only */ 424fbf5556SPeter Maydell }; 434fbf5556SPeter Maydell 444fbf5556SPeter Maydell 454fbf5556SPeter Maydell /* The Versatile/PB uses a slightly modified PL110 controller. */ 464fbf5556SPeter Maydell enum pl110_version 474fbf5556SPeter Maydell { 48c7bf3492SEduardo Habkost VERSION_PL110, 49c7bf3492SEduardo Habkost VERSION_PL110_VERSATILE, 50c7bf3492SEduardo Habkost VERSION_PL111 51bdd5003aSpbrook }; 52bdd5003aSpbrook 535d7a11e4SAndreas Färber #define TYPE_PL110 "pl110" 548063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110) 555d7a11e4SAndreas Färber 56db1015e9SEduardo Habkost struct PL110State { 575d7a11e4SAndreas Färber SysBusDevice parent_obj; 585d7a11e4SAndreas Färber 591a6b31ceSAvi Kivity MemoryRegion iomem; 60c1076c3eSPaolo Bonzini MemoryRegionSection fbsection; 61c78f7137SGerd Hoffmann QemuConsole *con; 6224da047aSLinus Walleij QEMUTimer *vblank_timer; 63c60e08d9Spbrook 644fbf5556SPeter Maydell int version; 65bdd5003aSpbrook uint32_t timing[4]; 66bdd5003aSpbrook uint32_t cr; 67bdd5003aSpbrook uint32_t upbase; 68bdd5003aSpbrook uint32_t lpbase; 69bdd5003aSpbrook uint32_t int_status; 70bdd5003aSpbrook uint32_t int_mask; 71bdd5003aSpbrook int cols; 72bdd5003aSpbrook int rows; 73bdd5003aSpbrook enum pl110_bppmode bpp; 74bdd5003aSpbrook int invalidate; 75242ea2c6SPeter Maydell uint32_t mux_ctrl; 766e4c0d1fSPeter Maydell uint32_t palette[256]; 776e4c0d1fSPeter Maydell uint32_t raw_palette[128]; 78d537cf6cSpbrook qemu_irq irq; 79*c2093660SPhilippe Mathieu-Daudé MemoryRegion *fbmem; 80db1015e9SEduardo Habkost }; 81bdd5003aSpbrook 82128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id); 83128939a9SPeter Maydell 848c60d065SPeter Maydell static const VMStateDescription vmstate_pl110 = { 858c60d065SPeter Maydell .name = "pl110", 86242ea2c6SPeter Maydell .version_id = 2, 878c60d065SPeter Maydell .minimum_version_id = 1, 88128939a9SPeter Maydell .post_load = vmstate_pl110_post_load, 89f0613160SRichard Henderson .fields = (const VMStateField[]) { 90513960eaSAndreas Färber VMSTATE_INT32(version, PL110State), 91513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(timing, PL110State, 4), 92513960eaSAndreas Färber VMSTATE_UINT32(cr, PL110State), 93513960eaSAndreas Färber VMSTATE_UINT32(upbase, PL110State), 94513960eaSAndreas Färber VMSTATE_UINT32(lpbase, PL110State), 95513960eaSAndreas Färber VMSTATE_UINT32(int_status, PL110State), 96513960eaSAndreas Färber VMSTATE_UINT32(int_mask, PL110State), 97513960eaSAndreas Färber VMSTATE_INT32(cols, PL110State), 98513960eaSAndreas Färber VMSTATE_INT32(rows, PL110State), 99513960eaSAndreas Färber VMSTATE_UINT32(bpp, PL110State), 100513960eaSAndreas Färber VMSTATE_INT32(invalidate, PL110State), 101513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(palette, PL110State, 256), 102513960eaSAndreas Färber VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128), 103513960eaSAndreas Färber VMSTATE_UINT32_V(mux_ctrl, PL110State, 2), 1048c60d065SPeter Maydell VMSTATE_END_OF_LIST() 1058c60d065SPeter Maydell } 1068c60d065SPeter Maydell }; 1078c60d065SPeter Maydell 108bdd5003aSpbrook static const unsigned char pl110_id[] = 109bdd5003aSpbrook { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 110bdd5003aSpbrook 1114fbf5556SPeter Maydell static const unsigned char pl111_id[] = { 1124fbf5556SPeter Maydell 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 1134fbf5556SPeter Maydell }; 1144fbf5556SPeter Maydell 115031c44e4SPeter Maydell 1164fbf5556SPeter Maydell /* Indexed by pl110_version */ 1174fbf5556SPeter Maydell static const unsigned char *idregs[] = { 1184fbf5556SPeter Maydell pl110_id, 119031c44e4SPeter Maydell /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board 120031c44e4SPeter Maydell * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware 121031c44e4SPeter Maydell * itself has the same ID values as a stock PL110, and guests (in 122031c44e4SPeter Maydell * particular Linux) rely on this. We emulate what the hardware does, 123031c44e4SPeter Maydell * rather than what the docs claim it ought to do. 124031c44e4SPeter Maydell */ 125031c44e4SPeter Maydell pl110_id, 1264fbf5556SPeter Maydell pl111_id 1274fbf5556SPeter Maydell }; 1284fbf5556SPeter Maydell 129560ebce6SPeter Maydell #define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0) 130560ebce6SPeter Maydell 131560ebce6SPeter Maydell #undef RGB 132560ebce6SPeter Maydell #define BORDER bgr 133560ebce6SPeter Maydell #define ORDER 0 13447b43a1fSPaolo Bonzini #include "pl110_template.h" 135560ebce6SPeter Maydell #define ORDER 1 136560ebce6SPeter Maydell #include "pl110_template.h" 137560ebce6SPeter Maydell #define ORDER 2 138560ebce6SPeter Maydell #include "pl110_template.h" 139560ebce6SPeter Maydell #undef BORDER 140560ebce6SPeter Maydell #define RGB 141560ebce6SPeter Maydell #define BORDER rgb 142560ebce6SPeter Maydell #define ORDER 0 143560ebce6SPeter Maydell #include "pl110_template.h" 144560ebce6SPeter Maydell #define ORDER 1 145560ebce6SPeter Maydell #include "pl110_template.h" 146560ebce6SPeter Maydell #define ORDER 2 147560ebce6SPeter Maydell #include "pl110_template.h" 148560ebce6SPeter Maydell #undef BORDER 149560ebce6SPeter Maydell 150560ebce6SPeter Maydell #undef COPY_PIXEL 151560ebce6SPeter Maydell 152ba1c16e4SPeter Maydell static drawfn pl110_draw_fn_32[48] = { 153ba1c16e4SPeter Maydell pl110_draw_line1_lblp_bgr, 154ba1c16e4SPeter Maydell pl110_draw_line2_lblp_bgr, 155ba1c16e4SPeter Maydell pl110_draw_line4_lblp_bgr, 156ba1c16e4SPeter Maydell pl110_draw_line8_lblp_bgr, 157ba1c16e4SPeter Maydell pl110_draw_line16_555_lblp_bgr, 158ba1c16e4SPeter Maydell pl110_draw_line32_lblp_bgr, 159ba1c16e4SPeter Maydell pl110_draw_line16_lblp_bgr, 160ba1c16e4SPeter Maydell pl110_draw_line12_lblp_bgr, 161ba1c16e4SPeter Maydell 162ba1c16e4SPeter Maydell pl110_draw_line1_bbbp_bgr, 163ba1c16e4SPeter Maydell pl110_draw_line2_bbbp_bgr, 164ba1c16e4SPeter Maydell pl110_draw_line4_bbbp_bgr, 165ba1c16e4SPeter Maydell pl110_draw_line8_bbbp_bgr, 166ba1c16e4SPeter Maydell pl110_draw_line16_555_bbbp_bgr, 167ba1c16e4SPeter Maydell pl110_draw_line32_bbbp_bgr, 168ba1c16e4SPeter Maydell pl110_draw_line16_bbbp_bgr, 169ba1c16e4SPeter Maydell pl110_draw_line12_bbbp_bgr, 170ba1c16e4SPeter Maydell 171ba1c16e4SPeter Maydell pl110_draw_line1_lbbp_bgr, 172ba1c16e4SPeter Maydell pl110_draw_line2_lbbp_bgr, 173ba1c16e4SPeter Maydell pl110_draw_line4_lbbp_bgr, 174ba1c16e4SPeter Maydell pl110_draw_line8_lbbp_bgr, 175ba1c16e4SPeter Maydell pl110_draw_line16_555_lbbp_bgr, 176ba1c16e4SPeter Maydell pl110_draw_line32_lbbp_bgr, 177ba1c16e4SPeter Maydell pl110_draw_line16_lbbp_bgr, 178ba1c16e4SPeter Maydell pl110_draw_line12_lbbp_bgr, 179ba1c16e4SPeter Maydell 180ba1c16e4SPeter Maydell pl110_draw_line1_lblp_rgb, 181ba1c16e4SPeter Maydell pl110_draw_line2_lblp_rgb, 182ba1c16e4SPeter Maydell pl110_draw_line4_lblp_rgb, 183ba1c16e4SPeter Maydell pl110_draw_line8_lblp_rgb, 184ba1c16e4SPeter Maydell pl110_draw_line16_555_lblp_rgb, 185ba1c16e4SPeter Maydell pl110_draw_line32_lblp_rgb, 186ba1c16e4SPeter Maydell pl110_draw_line16_lblp_rgb, 187ba1c16e4SPeter Maydell pl110_draw_line12_lblp_rgb, 188ba1c16e4SPeter Maydell 189ba1c16e4SPeter Maydell pl110_draw_line1_bbbp_rgb, 190ba1c16e4SPeter Maydell pl110_draw_line2_bbbp_rgb, 191ba1c16e4SPeter Maydell pl110_draw_line4_bbbp_rgb, 192ba1c16e4SPeter Maydell pl110_draw_line8_bbbp_rgb, 193ba1c16e4SPeter Maydell pl110_draw_line16_555_bbbp_rgb, 194ba1c16e4SPeter Maydell pl110_draw_line32_bbbp_rgb, 195ba1c16e4SPeter Maydell pl110_draw_line16_bbbp_rgb, 196ba1c16e4SPeter Maydell pl110_draw_line12_bbbp_rgb, 197ba1c16e4SPeter Maydell 198ba1c16e4SPeter Maydell pl110_draw_line1_lbbp_rgb, 199ba1c16e4SPeter Maydell pl110_draw_line2_lbbp_rgb, 200ba1c16e4SPeter Maydell pl110_draw_line4_lbbp_rgb, 201ba1c16e4SPeter Maydell pl110_draw_line8_lbbp_rgb, 202ba1c16e4SPeter Maydell pl110_draw_line16_555_lbbp_rgb, 203ba1c16e4SPeter Maydell pl110_draw_line32_lbbp_rgb, 204ba1c16e4SPeter Maydell pl110_draw_line16_lbbp_rgb, 205ba1c16e4SPeter Maydell pl110_draw_line12_lbbp_rgb, 206ba1c16e4SPeter Maydell }; 207bdd5003aSpbrook 208513960eaSAndreas Färber static int pl110_enabled(PL110State *s) 209bdd5003aSpbrook { 210bdd5003aSpbrook return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); 211bdd5003aSpbrook } 212bdd5003aSpbrook 21395219897Spbrook static void pl110_update_display(void *opaque) 214bdd5003aSpbrook { 215513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 216c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 217bdd5003aSpbrook drawfn fn; 218bdd5003aSpbrook int src_width; 219e9c05b42Sbalrog int bpp_offset; 220714fa308Spbrook int first; 221714fa308Spbrook int last; 222bdd5003aSpbrook 2235d7a11e4SAndreas Färber if (!pl110_enabled(s)) { 224bdd5003aSpbrook return; 2255d7a11e4SAndreas Färber } 2265d7a11e4SAndreas Färber 227e9c05b42Sbalrog if (s->cr & PL110_CR_BGR) 228e9c05b42Sbalrog bpp_offset = 0; 229bdd5003aSpbrook else 2304fbf5556SPeter Maydell bpp_offset = 24; 2314fbf5556SPeter Maydell 232c7bf3492SEduardo Habkost if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) { 2334fbf5556SPeter Maydell /* The PL110's native 16 bit mode is 5551; however 2344fbf5556SPeter Maydell * most boards with a PL110 implement an external 2354fbf5556SPeter Maydell * mux which allows bits to be reshuffled to give 2364fbf5556SPeter Maydell * 565 format. The mux is typically controlled by 2374fbf5556SPeter Maydell * an external system register. 238242ea2c6SPeter Maydell * This is controlled by a GPIO input pin 2394fbf5556SPeter Maydell * so boards can wire it up to their register. 2404fbf5556SPeter Maydell * 2414fbf5556SPeter Maydell * The PL111 straightforwardly implements both 2424fbf5556SPeter Maydell * 5551 and 565 under control of the bpp field 2434fbf5556SPeter Maydell * in the LCDControl register. 2444fbf5556SPeter Maydell */ 245242ea2c6SPeter Maydell switch (s->mux_ctrl) { 246242ea2c6SPeter Maydell case 3: /* 565 BGR */ 247242ea2c6SPeter Maydell bpp_offset = (BPP_16_565 - BPP_16); 248242ea2c6SPeter Maydell break; 249242ea2c6SPeter Maydell case 1: /* 5551 */ 250242ea2c6SPeter Maydell break; 251242ea2c6SPeter Maydell case 0: /* 888; also if we have loaded vmstate from an old version */ 252242ea2c6SPeter Maydell case 2: /* 565 RGB */ 253242ea2c6SPeter Maydell default: 254242ea2c6SPeter Maydell /* treat as 565 but honour BGR bit */ 2554fbf5556SPeter Maydell bpp_offset += (BPP_16_565 - BPP_16); 256242ea2c6SPeter Maydell break; 257242ea2c6SPeter Maydell } 2584fbf5556SPeter Maydell } 259e9c05b42Sbalrog 26062bdc8c1SPeter Maydell if (s->cr & PL110_CR_BEBO) { 26162bdc8c1SPeter Maydell fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset]; 26262bdc8c1SPeter Maydell } else if (s->cr & PL110_CR_BEPO) { 26362bdc8c1SPeter Maydell fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset]; 26462bdc8c1SPeter Maydell } else { 26562bdc8c1SPeter Maydell fn = pl110_draw_fn_32[s->bpp + bpp_offset]; 26662bdc8c1SPeter Maydell } 267bdd5003aSpbrook 268bdd5003aSpbrook src_width = s->cols; 269bdd5003aSpbrook switch (s->bpp) { 270bdd5003aSpbrook case BPP_1: 271bdd5003aSpbrook src_width >>= 3; 272bdd5003aSpbrook break; 273bdd5003aSpbrook case BPP_2: 274bdd5003aSpbrook src_width >>= 2; 275bdd5003aSpbrook break; 276bdd5003aSpbrook case BPP_4: 277bdd5003aSpbrook src_width >>= 1; 278bdd5003aSpbrook break; 279bdd5003aSpbrook case BPP_8: 280bdd5003aSpbrook break; 281bdd5003aSpbrook case BPP_16: 2824fbf5556SPeter Maydell case BPP_16_565: 2834fbf5556SPeter Maydell case BPP_12: 284bdd5003aSpbrook src_width <<= 1; 285bdd5003aSpbrook break; 286bdd5003aSpbrook case BPP_32: 287bdd5003aSpbrook src_width <<= 2; 288bdd5003aSpbrook break; 289bdd5003aSpbrook } 290714fa308Spbrook first = 0; 291c1076c3eSPaolo Bonzini if (s->invalidate) { 292c1076c3eSPaolo Bonzini framebuffer_update_memory_section(&s->fbsection, 293*c2093660SPhilippe Mathieu-Daudé s->fbmem, 294c1076c3eSPaolo Bonzini s->upbase, 295c1076c3eSPaolo Bonzini s->rows, src_width); 296c1076c3eSPaolo Bonzini } 297c1076c3eSPaolo Bonzini 298c1076c3eSPaolo Bonzini framebuffer_update_display(surface, &s->fbsection, 299c1076c3eSPaolo Bonzini s->cols, s->rows, 30062bdc8c1SPeter Maydell src_width, s->cols * 4, 0, 301714fa308Spbrook s->invalidate, 3026e4c0d1fSPeter Maydell fn, s->palette, 303714fa308Spbrook &first, &last); 304c1076c3eSPaolo Bonzini 305714fa308Spbrook if (first >= 0) { 306c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); 307bdd5003aSpbrook } 308714fa308Spbrook s->invalidate = 0; 309714fa308Spbrook } 310bdd5003aSpbrook 31195219897Spbrook static void pl110_invalidate_display(void * opaque) 312bdd5003aSpbrook { 313513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 314bdd5003aSpbrook s->invalidate = 1; 315bfdb3629SBlue Swirl if (pl110_enabled(s)) { 316c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 317bfdb3629SBlue Swirl } 318bdd5003aSpbrook } 319bdd5003aSpbrook 320513960eaSAndreas Färber static void pl110_update_palette(PL110State *s, int n) 321bdd5003aSpbrook { 322c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 323bdd5003aSpbrook int i; 324bdd5003aSpbrook uint32_t raw; 325bdd5003aSpbrook unsigned int r, g, b; 326bdd5003aSpbrook 3276e4c0d1fSPeter Maydell raw = s->raw_palette[n]; 328bdd5003aSpbrook n <<= 1; 329bdd5003aSpbrook for (i = 0; i < 2; i++) { 330bdd5003aSpbrook r = (raw & 0x1f) << 3; 331bdd5003aSpbrook raw >>= 5; 332bdd5003aSpbrook g = (raw & 0x1f) << 3; 333bdd5003aSpbrook raw >>= 5; 334bdd5003aSpbrook b = (raw & 0x1f) << 3; 335bdd5003aSpbrook /* The I bit is ignored. */ 336bdd5003aSpbrook raw >>= 6; 337c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 338bdd5003aSpbrook case 8: 3396e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel8(r, g, b); 340bdd5003aSpbrook break; 341bdd5003aSpbrook case 15: 3426e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel15(r, g, b); 343bdd5003aSpbrook break; 344bdd5003aSpbrook case 16: 3456e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel16(r, g, b); 346bdd5003aSpbrook break; 347bdd5003aSpbrook case 24: 348bdd5003aSpbrook case 32: 3496e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel32(r, g, b); 350bdd5003aSpbrook break; 351bdd5003aSpbrook } 352bdd5003aSpbrook n++; 353bdd5003aSpbrook } 354bdd5003aSpbrook } 355bdd5003aSpbrook 356513960eaSAndreas Färber static void pl110_resize(PL110State *s, int width, int height) 357bdd5003aSpbrook { 358bdd5003aSpbrook if (width != s->cols || height != s->rows) { 359bdd5003aSpbrook if (pl110_enabled(s)) { 360c78f7137SGerd Hoffmann qemu_console_resize(s->con, width, height); 361bdd5003aSpbrook } 362bdd5003aSpbrook } 363bdd5003aSpbrook s->cols = width; 364bdd5003aSpbrook s->rows = height; 365bdd5003aSpbrook } 366bdd5003aSpbrook 367bdd5003aSpbrook /* Update interrupts. */ 368513960eaSAndreas Färber static void pl110_update(PL110State *s) 369bdd5003aSpbrook { 37024da047aSLinus Walleij /* Raise IRQ if enabled and any status bit is 1 */ 37124da047aSLinus Walleij if (s->int_status & s->int_mask) { 37224da047aSLinus Walleij qemu_irq_raise(s->irq); 37324da047aSLinus Walleij } else { 37424da047aSLinus Walleij qemu_irq_lower(s->irq); 37524da047aSLinus Walleij } 37624da047aSLinus Walleij } 37724da047aSLinus Walleij 37824da047aSLinus Walleij static void pl110_vblank_interrupt(void *opaque) 37924da047aSLinus Walleij { 38024da047aSLinus Walleij PL110State *s = opaque; 38124da047aSLinus Walleij 38224da047aSLinus Walleij /* Fire the vertical compare and next base IRQs and re-arm */ 38324da047aSLinus Walleij s->int_status |= (PL110_IE_NB | PL110_IE_VC); 38424da047aSLinus Walleij timer_mod(s->vblank_timer, 38524da047aSLinus Walleij qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 38624da047aSLinus Walleij NANOSECONDS_PER_SECOND / 60); 38724da047aSLinus Walleij pl110_update(s); 388bdd5003aSpbrook } 389bdd5003aSpbrook 390a8170e5eSAvi Kivity static uint64_t pl110_read(void *opaque, hwaddr offset, 3911a6b31ceSAvi Kivity unsigned size) 392bdd5003aSpbrook { 393513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 394bdd5003aSpbrook 395bdd5003aSpbrook if (offset >= 0xfe0 && offset < 0x1000) { 3964fbf5556SPeter Maydell return idregs[s->version][(offset - 0xfe0) >> 2]; 397bdd5003aSpbrook } 398bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 3996e4c0d1fSPeter Maydell return s->raw_palette[(offset - 0x200) >> 2]; 400bdd5003aSpbrook } 401bdd5003aSpbrook switch (offset >> 2) { 402bdd5003aSpbrook case 0: /* LCDTiming0 */ 403bdd5003aSpbrook return s->timing[0]; 404bdd5003aSpbrook case 1: /* LCDTiming1 */ 405bdd5003aSpbrook return s->timing[1]; 406bdd5003aSpbrook case 2: /* LCDTiming2 */ 407bdd5003aSpbrook return s->timing[2]; 408bdd5003aSpbrook case 3: /* LCDTiming3 */ 409bdd5003aSpbrook return s->timing[3]; 410bdd5003aSpbrook case 4: /* LCDUPBASE */ 411bdd5003aSpbrook return s->upbase; 412bdd5003aSpbrook case 5: /* LCDLPBASE */ 413bdd5003aSpbrook return s->lpbase; 414bdd5003aSpbrook case 6: /* LCDIMSC */ 415c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 41664075cd7Spbrook return s->cr; 4174fbf5556SPeter Maydell } 418bdd5003aSpbrook return s->int_mask; 419bdd5003aSpbrook case 7: /* LCDControl */ 420c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 42164075cd7Spbrook return s->int_mask; 4224fbf5556SPeter Maydell } 423bdd5003aSpbrook return s->cr; 424bdd5003aSpbrook case 8: /* LCDRIS */ 425bdd5003aSpbrook return s->int_status; 426bdd5003aSpbrook case 9: /* LCDMIS */ 427bdd5003aSpbrook return s->int_status & s->int_mask; 428bdd5003aSpbrook case 11: /* LCDUPCURR */ 429bdd5003aSpbrook /* TODO: Implement vertical refresh. */ 430bdd5003aSpbrook return s->upbase; 431bdd5003aSpbrook case 12: /* LCDLPCURR */ 432bdd5003aSpbrook return s->lpbase; 433bdd5003aSpbrook default: 434375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 435375cb560SPeter Maydell "pl110_read: Bad offset %x\n", (int)offset); 436bdd5003aSpbrook return 0; 437bdd5003aSpbrook } 438bdd5003aSpbrook } 439bdd5003aSpbrook 440a8170e5eSAvi Kivity static void pl110_write(void *opaque, hwaddr offset, 4411a6b31ceSAvi Kivity uint64_t val, unsigned size) 442bdd5003aSpbrook { 443513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 444bdd5003aSpbrook int n; 445bdd5003aSpbrook 446bdd5003aSpbrook /* For simplicity invalidate the display whenever a control register 44766a0a2cbSDong Xu Wang is written to. */ 448bdd5003aSpbrook s->invalidate = 1; 449bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 4506e4c0d1fSPeter Maydell /* Palette. */ 451bdd5003aSpbrook n = (offset - 0x200) >> 2; 4526e4c0d1fSPeter Maydell s->raw_palette[(offset - 0x200) >> 2] = val; 4536e4c0d1fSPeter Maydell pl110_update_palette(s, n); 454e10c2bfbSpbrook return; 455bdd5003aSpbrook } 456bdd5003aSpbrook switch (offset >> 2) { 457bdd5003aSpbrook case 0: /* LCDTiming0 */ 458bdd5003aSpbrook s->timing[0] = val; 459bdd5003aSpbrook n = ((val & 0xfc) + 4) * 4; 460bdd5003aSpbrook pl110_resize(s, n, s->rows); 461bdd5003aSpbrook break; 462bdd5003aSpbrook case 1: /* LCDTiming1 */ 463bdd5003aSpbrook s->timing[1] = val; 464bdd5003aSpbrook n = (val & 0x3ff) + 1; 465bdd5003aSpbrook pl110_resize(s, s->cols, n); 466bdd5003aSpbrook break; 467bdd5003aSpbrook case 2: /* LCDTiming2 */ 468bdd5003aSpbrook s->timing[2] = val; 469bdd5003aSpbrook break; 470bdd5003aSpbrook case 3: /* LCDTiming3 */ 471bdd5003aSpbrook s->timing[3] = val; 472bdd5003aSpbrook break; 473bdd5003aSpbrook case 4: /* LCDUPBASE */ 474bdd5003aSpbrook s->upbase = val; 475bdd5003aSpbrook break; 476bdd5003aSpbrook case 5: /* LCDLPBASE */ 477bdd5003aSpbrook s->lpbase = val; 478bdd5003aSpbrook break; 479bdd5003aSpbrook case 6: /* LCDIMSC */ 480c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 481cdbdb648Spbrook goto control; 4824fbf5556SPeter Maydell } 483cdbdb648Spbrook imsc: 484bdd5003aSpbrook s->int_mask = val; 485bdd5003aSpbrook pl110_update(s); 486bdd5003aSpbrook break; 487bdd5003aSpbrook case 7: /* LCDControl */ 488c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) { 489cdbdb648Spbrook goto imsc; 4904fbf5556SPeter Maydell } 491cdbdb648Spbrook control: 492bdd5003aSpbrook s->cr = val; 493bdd5003aSpbrook s->bpp = (val >> 1) & 7; 494bdd5003aSpbrook if (pl110_enabled(s)) { 495c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 49624da047aSLinus Walleij timer_mod(s->vblank_timer, 49724da047aSLinus Walleij qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 49824da047aSLinus Walleij NANOSECONDS_PER_SECOND / 60); 49924da047aSLinus Walleij } else { 50024da047aSLinus Walleij timer_del(s->vblank_timer); 501bdd5003aSpbrook } 502bdd5003aSpbrook break; 503bdd5003aSpbrook case 10: /* LCDICR */ 504bdd5003aSpbrook s->int_status &= ~val; 505bdd5003aSpbrook pl110_update(s); 506bdd5003aSpbrook break; 507bdd5003aSpbrook default: 508375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 509375cb560SPeter Maydell "pl110_write: Bad offset %x\n", (int)offset); 510bdd5003aSpbrook } 511bdd5003aSpbrook } 512bdd5003aSpbrook 5131a6b31ceSAvi Kivity static const MemoryRegionOps pl110_ops = { 5141a6b31ceSAvi Kivity .read = pl110_read, 5151a6b31ceSAvi Kivity .write = pl110_write, 5161a6b31ceSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 517bdd5003aSpbrook }; 518bdd5003aSpbrook 519242ea2c6SPeter Maydell static void pl110_mux_ctrl_set(void *opaque, int line, int level) 520242ea2c6SPeter Maydell { 521513960eaSAndreas Färber PL110State *s = (PL110State *)opaque; 522242ea2c6SPeter Maydell s->mux_ctrl = level; 523242ea2c6SPeter Maydell } 524242ea2c6SPeter Maydell 525128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id) 526128939a9SPeter Maydell { 527513960eaSAndreas Färber PL110State *s = opaque; 528128939a9SPeter Maydell /* Make sure we redraw, and at the right size */ 529128939a9SPeter Maydell pl110_invalidate_display(s); 530128939a9SPeter Maydell return 0; 531128939a9SPeter Maydell } 532128939a9SPeter Maydell 533380cd056SGerd Hoffmann static const GraphicHwOps pl110_gfx_ops = { 534380cd056SGerd Hoffmann .invalidate = pl110_invalidate_display, 535380cd056SGerd Hoffmann .gfx_update = pl110_update_display, 536380cd056SGerd Hoffmann }; 537380cd056SGerd Hoffmann 538*c2093660SPhilippe Mathieu-Daudé static Property pl110_properties[] = { 539*c2093660SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem, 540*c2093660SPhilippe Mathieu-Daudé TYPE_MEMORY_REGION, MemoryRegion *), 541*c2093660SPhilippe Mathieu-Daudé DEFINE_PROP_END_OF_LIST(), 542*c2093660SPhilippe Mathieu-Daudé }; 543*c2093660SPhilippe Mathieu-Daudé 544caae8032Sxiaoqiang zhao static void pl110_realize(DeviceState *dev, Error **errp) 545bdd5003aSpbrook { 5465d7a11e4SAndreas Färber PL110State *s = PL110(dev); 547caae8032Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 548bdd5003aSpbrook 549*c2093660SPhilippe Mathieu-Daudé if (!s->fbmem) { 550*c2093660SPhilippe Mathieu-Daudé error_setg(errp, "'framebuffer-memory' property was not set"); 551*c2093660SPhilippe Mathieu-Daudé return; 552*c2093660SPhilippe Mathieu-Daudé } 553*c2093660SPhilippe Mathieu-Daudé 5543eadad55SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); 5555d7a11e4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem); 5565d7a11e4SAndreas Färber sysbus_init_irq(sbd, &s->irq); 55724da047aSLinus Walleij s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, 55824da047aSLinus Walleij pl110_vblank_interrupt, s); 5595d7a11e4SAndreas Färber qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1); 5605643706aSGerd Hoffmann s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s); 561bdd5003aSpbrook } 5622e9bdce5SPaul Brook 5635d7a11e4SAndreas Färber static void pl110_init(Object *obj) 5642e9bdce5SPaul Brook { 5655d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5665d7a11e4SAndreas Färber 567c7bf3492SEduardo Habkost s->version = VERSION_PL110; 5684fbf5556SPeter Maydell } 5694fbf5556SPeter Maydell 5705d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj) 5714fbf5556SPeter Maydell { 5725d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5735d7a11e4SAndreas Färber 574c7bf3492SEduardo Habkost s->version = VERSION_PL110_VERSATILE; 5755d7a11e4SAndreas Färber } 5765d7a11e4SAndreas Färber 5775d7a11e4SAndreas Färber static void pl111_init(Object *obj) 5785d7a11e4SAndreas Färber { 5795d7a11e4SAndreas Färber PL110State *s = PL110(obj); 5805d7a11e4SAndreas Färber 581c7bf3492SEduardo Habkost s->version = VERSION_PL111; 5822e9bdce5SPaul Brook } 5832e9bdce5SPaul Brook 584999e12bbSAnthony Liguori static void pl110_class_init(ObjectClass *klass, void *data) 585999e12bbSAnthony Liguori { 58639bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 587999e12bbSAnthony Liguori 588125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 58939bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 590caae8032Sxiaoqiang zhao dc->realize = pl110_realize; 591*c2093660SPhilippe Mathieu-Daudé device_class_set_props(dc, pl110_properties); 592999e12bbSAnthony Liguori } 593999e12bbSAnthony Liguori 5948c43a6f0SAndreas Färber static const TypeInfo pl110_info = { 5955d7a11e4SAndreas Färber .name = TYPE_PL110, 59639bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 597513960eaSAndreas Färber .instance_size = sizeof(PL110State), 5985d7a11e4SAndreas Färber .instance_init = pl110_init, 599999e12bbSAnthony Liguori .class_init = pl110_class_init, 6008c60d065SPeter Maydell }; 6018c60d065SPeter Maydell 6028c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = { 603999e12bbSAnthony Liguori .name = "pl110_versatile", 6045d7a11e4SAndreas Färber .parent = TYPE_PL110, 6055d7a11e4SAndreas Färber .instance_init = pl110_versatile_init, 6068c60d065SPeter Maydell }; 6078c60d065SPeter Maydell 6088c43a6f0SAndreas Färber static const TypeInfo pl111_info = { 609999e12bbSAnthony Liguori .name = "pl111", 6105d7a11e4SAndreas Färber .parent = TYPE_PL110, 6115d7a11e4SAndreas Färber .instance_init = pl111_init, 6124fbf5556SPeter Maydell }; 6134fbf5556SPeter Maydell 61483f7d43aSAndreas Färber static void pl110_register_types(void) 6152e9bdce5SPaul Brook { 61639bffca2SAnthony Liguori type_register_static(&pl110_info); 61739bffca2SAnthony Liguori type_register_static(&pl110_versatile_info); 61839bffca2SAnthony Liguori type_register_static(&pl111_info); 6192e9bdce5SPaul Brook } 6202e9bdce5SPaul Brook 62183f7d43aSAndreas Färber type_init(pl110_register_types) 622