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 1083c9f4caSPaolo Bonzini #include "hw/sysbus.h" 1128ecbaeeSPaolo Bonzini #include "ui/console.h" 1247b43a1fSPaolo Bonzini #include "framebuffer.h" 1328ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h" 14bdd5003aSpbrook 15bdd5003aSpbrook #define PL110_CR_EN 0x001 16e9c05b42Sbalrog #define PL110_CR_BGR 0x100 17bdd5003aSpbrook #define PL110_CR_BEBO 0x200 18bdd5003aSpbrook #define PL110_CR_BEPO 0x400 19bdd5003aSpbrook #define PL110_CR_PWR 0x800 20bdd5003aSpbrook 21bdd5003aSpbrook enum pl110_bppmode 22bdd5003aSpbrook { 23bdd5003aSpbrook BPP_1, 24bdd5003aSpbrook BPP_2, 25bdd5003aSpbrook BPP_4, 26bdd5003aSpbrook BPP_8, 27bdd5003aSpbrook BPP_16, 284fbf5556SPeter Maydell BPP_32, 294fbf5556SPeter Maydell BPP_16_565, /* PL111 only */ 304fbf5556SPeter Maydell BPP_12 /* PL111 only */ 314fbf5556SPeter Maydell }; 324fbf5556SPeter Maydell 334fbf5556SPeter Maydell 344fbf5556SPeter Maydell /* The Versatile/PB uses a slightly modified PL110 controller. */ 354fbf5556SPeter Maydell enum pl110_version 364fbf5556SPeter Maydell { 374fbf5556SPeter Maydell PL110, 384fbf5556SPeter Maydell PL110_VERSATILE, 394fbf5556SPeter Maydell PL111 40bdd5003aSpbrook }; 41bdd5003aSpbrook 42bdd5003aSpbrook typedef struct { 432e9bdce5SPaul Brook SysBusDevice busdev; 441a6b31ceSAvi Kivity MemoryRegion iomem; 45c78f7137SGerd Hoffmann QemuConsole *con; 46c60e08d9Spbrook 474fbf5556SPeter Maydell int version; 48bdd5003aSpbrook uint32_t timing[4]; 49bdd5003aSpbrook uint32_t cr; 50bdd5003aSpbrook uint32_t upbase; 51bdd5003aSpbrook uint32_t lpbase; 52bdd5003aSpbrook uint32_t int_status; 53bdd5003aSpbrook uint32_t int_mask; 54bdd5003aSpbrook int cols; 55bdd5003aSpbrook int rows; 56bdd5003aSpbrook enum pl110_bppmode bpp; 57bdd5003aSpbrook int invalidate; 58242ea2c6SPeter Maydell uint32_t mux_ctrl; 596e4c0d1fSPeter Maydell uint32_t palette[256]; 606e4c0d1fSPeter Maydell uint32_t raw_palette[128]; 61d537cf6cSpbrook qemu_irq irq; 62bdd5003aSpbrook } pl110_state; 63bdd5003aSpbrook 64128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id); 65128939a9SPeter Maydell 668c60d065SPeter Maydell static const VMStateDescription vmstate_pl110 = { 678c60d065SPeter Maydell .name = "pl110", 68242ea2c6SPeter Maydell .version_id = 2, 698c60d065SPeter Maydell .minimum_version_id = 1, 70128939a9SPeter Maydell .post_load = vmstate_pl110_post_load, 718c60d065SPeter Maydell .fields = (VMStateField[]) { 724fbf5556SPeter Maydell VMSTATE_INT32(version, pl110_state), 738c60d065SPeter Maydell VMSTATE_UINT32_ARRAY(timing, pl110_state, 4), 748c60d065SPeter Maydell VMSTATE_UINT32(cr, pl110_state), 758c60d065SPeter Maydell VMSTATE_UINT32(upbase, pl110_state), 768c60d065SPeter Maydell VMSTATE_UINT32(lpbase, pl110_state), 778c60d065SPeter Maydell VMSTATE_UINT32(int_status, pl110_state), 788c60d065SPeter Maydell VMSTATE_UINT32(int_mask, pl110_state), 798c60d065SPeter Maydell VMSTATE_INT32(cols, pl110_state), 808c60d065SPeter Maydell VMSTATE_INT32(rows, pl110_state), 818c60d065SPeter Maydell VMSTATE_UINT32(bpp, pl110_state), 828c60d065SPeter Maydell VMSTATE_INT32(invalidate, pl110_state), 836e4c0d1fSPeter Maydell VMSTATE_UINT32_ARRAY(palette, pl110_state, 256), 846e4c0d1fSPeter Maydell VMSTATE_UINT32_ARRAY(raw_palette, pl110_state, 128), 85242ea2c6SPeter Maydell VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2), 868c60d065SPeter Maydell VMSTATE_END_OF_LIST() 878c60d065SPeter Maydell } 888c60d065SPeter Maydell }; 898c60d065SPeter Maydell 90bdd5003aSpbrook static const unsigned char pl110_id[] = 91bdd5003aSpbrook { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 92bdd5003aSpbrook 93cdbdb648Spbrook /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board 94cdbdb648Spbrook has a different ID. However Linux only looks for the normal ID. */ 95cdbdb648Spbrook #if 0 96cdbdb648Spbrook static const unsigned char pl110_versatile_id[] = 97cdbdb648Spbrook { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; 98cdbdb648Spbrook #else 99cdbdb648Spbrook #define pl110_versatile_id pl110_id 100cdbdb648Spbrook #endif 101cdbdb648Spbrook 1024fbf5556SPeter Maydell static const unsigned char pl111_id[] = { 1034fbf5556SPeter Maydell 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1 1044fbf5556SPeter Maydell }; 1054fbf5556SPeter Maydell 1064fbf5556SPeter Maydell /* Indexed by pl110_version */ 1074fbf5556SPeter Maydell static const unsigned char *idregs[] = { 1084fbf5556SPeter Maydell pl110_id, 1094fbf5556SPeter Maydell pl110_versatile_id, 1104fbf5556SPeter Maydell pl111_id 1114fbf5556SPeter Maydell }; 1124fbf5556SPeter Maydell 113bdd5003aSpbrook #define BITS 8 11447b43a1fSPaolo Bonzini #include "pl110_template.h" 115bdd5003aSpbrook #define BITS 15 11647b43a1fSPaolo Bonzini #include "pl110_template.h" 117bdd5003aSpbrook #define BITS 16 11847b43a1fSPaolo Bonzini #include "pl110_template.h" 119bdd5003aSpbrook #define BITS 24 12047b43a1fSPaolo Bonzini #include "pl110_template.h" 121bdd5003aSpbrook #define BITS 32 12247b43a1fSPaolo Bonzini #include "pl110_template.h" 123bdd5003aSpbrook 124bdd5003aSpbrook static int pl110_enabled(pl110_state *s) 125bdd5003aSpbrook { 126bdd5003aSpbrook return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR); 127bdd5003aSpbrook } 128bdd5003aSpbrook 12995219897Spbrook static void pl110_update_display(void *opaque) 130bdd5003aSpbrook { 131bdd5003aSpbrook pl110_state *s = (pl110_state *)opaque; 132c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 133bdd5003aSpbrook drawfn* fntable; 134bdd5003aSpbrook drawfn fn; 135bdd5003aSpbrook int dest_width; 136bdd5003aSpbrook int src_width; 137e9c05b42Sbalrog int bpp_offset; 138714fa308Spbrook int first; 139714fa308Spbrook int last; 140bdd5003aSpbrook 141bdd5003aSpbrook if (!pl110_enabled(s)) 142bdd5003aSpbrook return; 143bdd5003aSpbrook 144c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 145af2f6733Spbrook case 0: 146af2f6733Spbrook return; 147bdd5003aSpbrook case 8: 148bdd5003aSpbrook fntable = pl110_draw_fn_8; 149bdd5003aSpbrook dest_width = 1; 150bdd5003aSpbrook break; 151bdd5003aSpbrook case 15: 152bdd5003aSpbrook fntable = pl110_draw_fn_15; 153bdd5003aSpbrook dest_width = 2; 154bdd5003aSpbrook break; 155bdd5003aSpbrook case 16: 156bdd5003aSpbrook fntable = pl110_draw_fn_16; 157bdd5003aSpbrook dest_width = 2; 158bdd5003aSpbrook break; 159bdd5003aSpbrook case 24: 160bdd5003aSpbrook fntable = pl110_draw_fn_24; 161bdd5003aSpbrook dest_width = 3; 162bdd5003aSpbrook break; 163bdd5003aSpbrook case 32: 164bdd5003aSpbrook fntable = pl110_draw_fn_32; 165bdd5003aSpbrook dest_width = 4; 166bdd5003aSpbrook break; 167bdd5003aSpbrook default: 168af2f6733Spbrook fprintf(stderr, "pl110: Bad color depth\n"); 169bdd5003aSpbrook exit(1); 170bdd5003aSpbrook } 171e9c05b42Sbalrog if (s->cr & PL110_CR_BGR) 172e9c05b42Sbalrog bpp_offset = 0; 173bdd5003aSpbrook else 1744fbf5556SPeter Maydell bpp_offset = 24; 1754fbf5556SPeter Maydell 1764fbf5556SPeter Maydell if ((s->version != PL111) && (s->bpp == BPP_16)) { 1774fbf5556SPeter Maydell /* The PL110's native 16 bit mode is 5551; however 1784fbf5556SPeter Maydell * most boards with a PL110 implement an external 1794fbf5556SPeter Maydell * mux which allows bits to be reshuffled to give 1804fbf5556SPeter Maydell * 565 format. The mux is typically controlled by 1814fbf5556SPeter Maydell * an external system register. 182242ea2c6SPeter Maydell * This is controlled by a GPIO input pin 1834fbf5556SPeter Maydell * so boards can wire it up to their register. 1844fbf5556SPeter Maydell * 1854fbf5556SPeter Maydell * The PL111 straightforwardly implements both 1864fbf5556SPeter Maydell * 5551 and 565 under control of the bpp field 1874fbf5556SPeter Maydell * in the LCDControl register. 1884fbf5556SPeter Maydell */ 189242ea2c6SPeter Maydell switch (s->mux_ctrl) { 190242ea2c6SPeter Maydell case 3: /* 565 BGR */ 191242ea2c6SPeter Maydell bpp_offset = (BPP_16_565 - BPP_16); 192242ea2c6SPeter Maydell break; 193242ea2c6SPeter Maydell case 1: /* 5551 */ 194242ea2c6SPeter Maydell break; 195242ea2c6SPeter Maydell case 0: /* 888; also if we have loaded vmstate from an old version */ 196242ea2c6SPeter Maydell case 2: /* 565 RGB */ 197242ea2c6SPeter Maydell default: 198242ea2c6SPeter Maydell /* treat as 565 but honour BGR bit */ 1994fbf5556SPeter Maydell bpp_offset += (BPP_16_565 - BPP_16); 200242ea2c6SPeter Maydell break; 201242ea2c6SPeter Maydell } 2024fbf5556SPeter Maydell } 203e9c05b42Sbalrog 204e9c05b42Sbalrog if (s->cr & PL110_CR_BEBO) 2054fbf5556SPeter Maydell fn = fntable[s->bpp + 8 + bpp_offset]; 206e9c05b42Sbalrog else if (s->cr & PL110_CR_BEPO) 2074fbf5556SPeter Maydell fn = fntable[s->bpp + 16 + bpp_offset]; 208e9c05b42Sbalrog else 209e9c05b42Sbalrog fn = fntable[s->bpp + bpp_offset]; 210bdd5003aSpbrook 211bdd5003aSpbrook src_width = s->cols; 212bdd5003aSpbrook switch (s->bpp) { 213bdd5003aSpbrook case BPP_1: 214bdd5003aSpbrook src_width >>= 3; 215bdd5003aSpbrook break; 216bdd5003aSpbrook case BPP_2: 217bdd5003aSpbrook src_width >>= 2; 218bdd5003aSpbrook break; 219bdd5003aSpbrook case BPP_4: 220bdd5003aSpbrook src_width >>= 1; 221bdd5003aSpbrook break; 222bdd5003aSpbrook case BPP_8: 223bdd5003aSpbrook break; 224bdd5003aSpbrook case BPP_16: 2254fbf5556SPeter Maydell case BPP_16_565: 2264fbf5556SPeter Maydell case BPP_12: 227bdd5003aSpbrook src_width <<= 1; 228bdd5003aSpbrook break; 229bdd5003aSpbrook case BPP_32: 230bdd5003aSpbrook src_width <<= 2; 231bdd5003aSpbrook break; 232bdd5003aSpbrook } 233bdd5003aSpbrook dest_width *= s->cols; 234714fa308Spbrook first = 0; 235c78f7137SGerd Hoffmann framebuffer_update_display(surface, sysbus_address_space(&s->busdev), 236714fa308Spbrook s->upbase, s->cols, s->rows, 237714fa308Spbrook src_width, dest_width, 0, 238714fa308Spbrook s->invalidate, 2396e4c0d1fSPeter Maydell fn, s->palette, 240714fa308Spbrook &first, &last); 241714fa308Spbrook if (first >= 0) { 242c78f7137SGerd Hoffmann dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1); 243bdd5003aSpbrook } 244714fa308Spbrook s->invalidate = 0; 245714fa308Spbrook } 246bdd5003aSpbrook 24795219897Spbrook static void pl110_invalidate_display(void * opaque) 248bdd5003aSpbrook { 249bdd5003aSpbrook pl110_state *s = (pl110_state *)opaque; 250bdd5003aSpbrook s->invalidate = 1; 251bfdb3629SBlue Swirl if (pl110_enabled(s)) { 252c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 253bfdb3629SBlue Swirl } 254bdd5003aSpbrook } 255bdd5003aSpbrook 2566e4c0d1fSPeter Maydell static void pl110_update_palette(pl110_state *s, int n) 257bdd5003aSpbrook { 258c78f7137SGerd Hoffmann DisplaySurface *surface = qemu_console_surface(s->con); 259bdd5003aSpbrook int i; 260bdd5003aSpbrook uint32_t raw; 261bdd5003aSpbrook unsigned int r, g, b; 262bdd5003aSpbrook 2636e4c0d1fSPeter Maydell raw = s->raw_palette[n]; 264bdd5003aSpbrook n <<= 1; 265bdd5003aSpbrook for (i = 0; i < 2; i++) { 266bdd5003aSpbrook r = (raw & 0x1f) << 3; 267bdd5003aSpbrook raw >>= 5; 268bdd5003aSpbrook g = (raw & 0x1f) << 3; 269bdd5003aSpbrook raw >>= 5; 270bdd5003aSpbrook b = (raw & 0x1f) << 3; 271bdd5003aSpbrook /* The I bit is ignored. */ 272bdd5003aSpbrook raw >>= 6; 273c78f7137SGerd Hoffmann switch (surface_bits_per_pixel(surface)) { 274bdd5003aSpbrook case 8: 2756e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel8(r, g, b); 276bdd5003aSpbrook break; 277bdd5003aSpbrook case 15: 2786e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel15(r, g, b); 279bdd5003aSpbrook break; 280bdd5003aSpbrook case 16: 2816e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel16(r, g, b); 282bdd5003aSpbrook break; 283bdd5003aSpbrook case 24: 284bdd5003aSpbrook case 32: 2856e4c0d1fSPeter Maydell s->palette[n] = rgb_to_pixel32(r, g, b); 286bdd5003aSpbrook break; 287bdd5003aSpbrook } 288bdd5003aSpbrook n++; 289bdd5003aSpbrook } 290bdd5003aSpbrook } 291bdd5003aSpbrook 292bdd5003aSpbrook static void pl110_resize(pl110_state *s, int width, int height) 293bdd5003aSpbrook { 294bdd5003aSpbrook if (width != s->cols || height != s->rows) { 295bdd5003aSpbrook if (pl110_enabled(s)) { 296c78f7137SGerd Hoffmann qemu_console_resize(s->con, width, height); 297bdd5003aSpbrook } 298bdd5003aSpbrook } 299bdd5003aSpbrook s->cols = width; 300bdd5003aSpbrook s->rows = height; 301bdd5003aSpbrook } 302bdd5003aSpbrook 303bdd5003aSpbrook /* Update interrupts. */ 304bdd5003aSpbrook static void pl110_update(pl110_state *s) 305bdd5003aSpbrook { 306bdd5003aSpbrook /* TODO: Implement interrupts. */ 307bdd5003aSpbrook } 308bdd5003aSpbrook 309a8170e5eSAvi Kivity static uint64_t pl110_read(void *opaque, hwaddr offset, 3101a6b31ceSAvi Kivity unsigned size) 311bdd5003aSpbrook { 312bdd5003aSpbrook pl110_state *s = (pl110_state *)opaque; 313bdd5003aSpbrook 314bdd5003aSpbrook if (offset >= 0xfe0 && offset < 0x1000) { 3154fbf5556SPeter Maydell return idregs[s->version][(offset - 0xfe0) >> 2]; 316bdd5003aSpbrook } 317bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 3186e4c0d1fSPeter Maydell return s->raw_palette[(offset - 0x200) >> 2]; 319bdd5003aSpbrook } 320bdd5003aSpbrook switch (offset >> 2) { 321bdd5003aSpbrook case 0: /* LCDTiming0 */ 322bdd5003aSpbrook return s->timing[0]; 323bdd5003aSpbrook case 1: /* LCDTiming1 */ 324bdd5003aSpbrook return s->timing[1]; 325bdd5003aSpbrook case 2: /* LCDTiming2 */ 326bdd5003aSpbrook return s->timing[2]; 327bdd5003aSpbrook case 3: /* LCDTiming3 */ 328bdd5003aSpbrook return s->timing[3]; 329bdd5003aSpbrook case 4: /* LCDUPBASE */ 330bdd5003aSpbrook return s->upbase; 331bdd5003aSpbrook case 5: /* LCDLPBASE */ 332bdd5003aSpbrook return s->lpbase; 333bdd5003aSpbrook case 6: /* LCDIMSC */ 3344fbf5556SPeter Maydell if (s->version != PL110) { 33564075cd7Spbrook return s->cr; 3364fbf5556SPeter Maydell } 337bdd5003aSpbrook return s->int_mask; 338bdd5003aSpbrook case 7: /* LCDControl */ 3394fbf5556SPeter Maydell if (s->version != PL110) { 34064075cd7Spbrook return s->int_mask; 3414fbf5556SPeter Maydell } 342bdd5003aSpbrook return s->cr; 343bdd5003aSpbrook case 8: /* LCDRIS */ 344bdd5003aSpbrook return s->int_status; 345bdd5003aSpbrook case 9: /* LCDMIS */ 346bdd5003aSpbrook return s->int_status & s->int_mask; 347bdd5003aSpbrook case 11: /* LCDUPCURR */ 348bdd5003aSpbrook /* TODO: Implement vertical refresh. */ 349bdd5003aSpbrook return s->upbase; 350bdd5003aSpbrook case 12: /* LCDLPCURR */ 351bdd5003aSpbrook return s->lpbase; 352bdd5003aSpbrook default: 353375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 354375cb560SPeter Maydell "pl110_read: Bad offset %x\n", (int)offset); 355bdd5003aSpbrook return 0; 356bdd5003aSpbrook } 357bdd5003aSpbrook } 358bdd5003aSpbrook 359a8170e5eSAvi Kivity static void pl110_write(void *opaque, hwaddr offset, 3601a6b31ceSAvi Kivity uint64_t val, unsigned size) 361bdd5003aSpbrook { 362bdd5003aSpbrook pl110_state *s = (pl110_state *)opaque; 363bdd5003aSpbrook int n; 364bdd5003aSpbrook 365bdd5003aSpbrook /* For simplicity invalidate the display whenever a control register 36666a0a2cbSDong Xu Wang is written to. */ 367bdd5003aSpbrook s->invalidate = 1; 368bdd5003aSpbrook if (offset >= 0x200 && offset < 0x400) { 3696e4c0d1fSPeter Maydell /* Palette. */ 370bdd5003aSpbrook n = (offset - 0x200) >> 2; 3716e4c0d1fSPeter Maydell s->raw_palette[(offset - 0x200) >> 2] = val; 3726e4c0d1fSPeter Maydell pl110_update_palette(s, n); 373e10c2bfbSpbrook return; 374bdd5003aSpbrook } 375bdd5003aSpbrook switch (offset >> 2) { 376bdd5003aSpbrook case 0: /* LCDTiming0 */ 377bdd5003aSpbrook s->timing[0] = val; 378bdd5003aSpbrook n = ((val & 0xfc) + 4) * 4; 379bdd5003aSpbrook pl110_resize(s, n, s->rows); 380bdd5003aSpbrook break; 381bdd5003aSpbrook case 1: /* LCDTiming1 */ 382bdd5003aSpbrook s->timing[1] = val; 383bdd5003aSpbrook n = (val & 0x3ff) + 1; 384bdd5003aSpbrook pl110_resize(s, s->cols, n); 385bdd5003aSpbrook break; 386bdd5003aSpbrook case 2: /* LCDTiming2 */ 387bdd5003aSpbrook s->timing[2] = val; 388bdd5003aSpbrook break; 389bdd5003aSpbrook case 3: /* LCDTiming3 */ 390bdd5003aSpbrook s->timing[3] = val; 391bdd5003aSpbrook break; 392bdd5003aSpbrook case 4: /* LCDUPBASE */ 393bdd5003aSpbrook s->upbase = val; 394bdd5003aSpbrook break; 395bdd5003aSpbrook case 5: /* LCDLPBASE */ 396bdd5003aSpbrook s->lpbase = val; 397bdd5003aSpbrook break; 398bdd5003aSpbrook case 6: /* LCDIMSC */ 3994fbf5556SPeter Maydell if (s->version != PL110) { 400cdbdb648Spbrook goto control; 4014fbf5556SPeter Maydell } 402cdbdb648Spbrook imsc: 403bdd5003aSpbrook s->int_mask = val; 404bdd5003aSpbrook pl110_update(s); 405bdd5003aSpbrook break; 406bdd5003aSpbrook case 7: /* LCDControl */ 4074fbf5556SPeter Maydell if (s->version != PL110) { 408cdbdb648Spbrook goto imsc; 4094fbf5556SPeter Maydell } 410cdbdb648Spbrook control: 411bdd5003aSpbrook s->cr = val; 412bdd5003aSpbrook s->bpp = (val >> 1) & 7; 413bdd5003aSpbrook if (pl110_enabled(s)) { 414c78f7137SGerd Hoffmann qemu_console_resize(s->con, s->cols, s->rows); 415bdd5003aSpbrook } 416bdd5003aSpbrook break; 417bdd5003aSpbrook case 10: /* LCDICR */ 418bdd5003aSpbrook s->int_status &= ~val; 419bdd5003aSpbrook pl110_update(s); 420bdd5003aSpbrook break; 421bdd5003aSpbrook default: 422375cb560SPeter Maydell qemu_log_mask(LOG_GUEST_ERROR, 423375cb560SPeter Maydell "pl110_write: Bad offset %x\n", (int)offset); 424bdd5003aSpbrook } 425bdd5003aSpbrook } 426bdd5003aSpbrook 4271a6b31ceSAvi Kivity static const MemoryRegionOps pl110_ops = { 4281a6b31ceSAvi Kivity .read = pl110_read, 4291a6b31ceSAvi Kivity .write = pl110_write, 4301a6b31ceSAvi Kivity .endianness = DEVICE_NATIVE_ENDIAN, 431bdd5003aSpbrook }; 432bdd5003aSpbrook 433242ea2c6SPeter Maydell static void pl110_mux_ctrl_set(void *opaque, int line, int level) 434242ea2c6SPeter Maydell { 435242ea2c6SPeter Maydell pl110_state *s = (pl110_state *)opaque; 436242ea2c6SPeter Maydell s->mux_ctrl = level; 437242ea2c6SPeter Maydell } 438242ea2c6SPeter Maydell 439128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id) 440128939a9SPeter Maydell { 441128939a9SPeter Maydell pl110_state *s = opaque; 442128939a9SPeter Maydell /* Make sure we redraw, and at the right size */ 443128939a9SPeter Maydell pl110_invalidate_display(s); 444128939a9SPeter Maydell return 0; 445128939a9SPeter Maydell } 446128939a9SPeter Maydell 447380cd056SGerd Hoffmann static const GraphicHwOps pl110_gfx_ops = { 448380cd056SGerd Hoffmann .invalidate = pl110_invalidate_display, 449380cd056SGerd Hoffmann .gfx_update = pl110_update_display, 450380cd056SGerd Hoffmann }; 451380cd056SGerd Hoffmann 45281a322d4SGerd Hoffmann static int pl110_init(SysBusDevice *dev) 453bdd5003aSpbrook { 4542e9bdce5SPaul Brook pl110_state *s = FROM_SYSBUS(pl110_state, dev); 455bdd5003aSpbrook 456*3eadad55SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000); 457750ecd44SAvi Kivity sysbus_init_mmio(dev, &s->iomem); 4582e9bdce5SPaul Brook sysbus_init_irq(dev, &s->irq); 459242ea2c6SPeter Maydell qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1); 460aa2beaa1SGerd Hoffmann s->con = graphic_console_init(DEVICE(dev), &pl110_gfx_ops, s); 46181a322d4SGerd Hoffmann return 0; 462bdd5003aSpbrook } 4632e9bdce5SPaul Brook 46481a322d4SGerd Hoffmann static int pl110_versatile_init(SysBusDevice *dev) 4652e9bdce5SPaul Brook { 4662e9bdce5SPaul Brook pl110_state *s = FROM_SYSBUS(pl110_state, dev); 4674fbf5556SPeter Maydell s->version = PL110_VERSATILE; 4684fbf5556SPeter Maydell return pl110_init(dev); 4694fbf5556SPeter Maydell } 4704fbf5556SPeter Maydell 4714fbf5556SPeter Maydell static int pl111_init(SysBusDevice *dev) 4724fbf5556SPeter Maydell { 4734fbf5556SPeter Maydell pl110_state *s = FROM_SYSBUS(pl110_state, dev); 4744fbf5556SPeter Maydell s->version = PL111; 47581a322d4SGerd Hoffmann return pl110_init(dev); 4762e9bdce5SPaul Brook } 4772e9bdce5SPaul Brook 478999e12bbSAnthony Liguori static void pl110_class_init(ObjectClass *klass, void *data) 479999e12bbSAnthony Liguori { 48039bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 481999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 482999e12bbSAnthony Liguori 483999e12bbSAnthony Liguori k->init = pl110_init; 48439bffca2SAnthony Liguori dc->no_user = 1; 48539bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 486999e12bbSAnthony Liguori } 487999e12bbSAnthony Liguori 4888c43a6f0SAndreas Färber static const TypeInfo pl110_info = { 489999e12bbSAnthony Liguori .name = "pl110", 49039bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 49139bffca2SAnthony Liguori .instance_size = sizeof(pl110_state), 492999e12bbSAnthony Liguori .class_init = pl110_class_init, 4938c60d065SPeter Maydell }; 4948c60d065SPeter Maydell 495999e12bbSAnthony Liguori static void pl110_versatile_class_init(ObjectClass *klass, void *data) 496999e12bbSAnthony Liguori { 49739bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 498999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 499999e12bbSAnthony Liguori 500999e12bbSAnthony Liguori k->init = pl110_versatile_init; 50139bffca2SAnthony Liguori dc->no_user = 1; 50239bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 503999e12bbSAnthony Liguori } 504999e12bbSAnthony Liguori 5058c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = { 506999e12bbSAnthony Liguori .name = "pl110_versatile", 50739bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 50839bffca2SAnthony Liguori .instance_size = sizeof(pl110_state), 509999e12bbSAnthony Liguori .class_init = pl110_versatile_class_init, 5108c60d065SPeter Maydell }; 5118c60d065SPeter Maydell 512999e12bbSAnthony Liguori static void pl111_class_init(ObjectClass *klass, void *data) 513999e12bbSAnthony Liguori { 51439bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass); 515999e12bbSAnthony Liguori SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 516999e12bbSAnthony Liguori 517999e12bbSAnthony Liguori k->init = pl111_init; 51839bffca2SAnthony Liguori dc->no_user = 1; 51939bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110; 520999e12bbSAnthony Liguori } 521999e12bbSAnthony Liguori 5228c43a6f0SAndreas Färber static const TypeInfo pl111_info = { 523999e12bbSAnthony Liguori .name = "pl111", 52439bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE, 52539bffca2SAnthony Liguori .instance_size = sizeof(pl110_state), 526999e12bbSAnthony Liguori .class_init = pl111_class_init, 5274fbf5556SPeter Maydell }; 5284fbf5556SPeter Maydell 52983f7d43aSAndreas Färber static void pl110_register_types(void) 5302e9bdce5SPaul Brook { 53139bffca2SAnthony Liguori type_register_static(&pl110_info); 53239bffca2SAnthony Liguori type_register_static(&pl110_versatile_info); 53339bffca2SAnthony Liguori type_register_static(&pl111_info); 5342e9bdce5SPaul Brook } 5352e9bdce5SPaul Brook 53683f7d43aSAndreas Färber type_init(pl110_register_types) 537