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"
13c2093660SPhilippe 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"
21c2093660SPhilippe 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;
79c2093660SPhilippe 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
pl110_enabled(PL110State * s)208513960eaSAndreas Färber static int pl110_enabled(PL110State *s)
209bdd5003aSpbrook {
210bdd5003aSpbrook return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
211bdd5003aSpbrook }
212bdd5003aSpbrook
pl110_update_display(void * opaque)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,
293c2093660SPhilippe 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
pl110_invalidate_display(void * opaque)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
pl110_update_palette(PL110State * s,int n)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
pl110_resize(PL110State * s,int width,int height)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. */
pl110_update(PL110State * s)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
pl110_vblank_interrupt(void * opaque)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
pl110_read(void * opaque,hwaddr offset,unsigned size)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
pl110_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)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
pl110_mux_ctrl_set(void * opaque,int line,int level)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
vmstate_pl110_post_load(void * opaque,int version_id)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
538d432edd5SRichard Henderson static const Property pl110_properties[] = {
539c2093660SPhilippe Mathieu-Daudé DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem,
540c2093660SPhilippe Mathieu-Daudé TYPE_MEMORY_REGION, MemoryRegion *),
541c2093660SPhilippe Mathieu-Daudé };
542c2093660SPhilippe Mathieu-Daudé
pl110_realize(DeviceState * dev,Error ** errp)543caae8032Sxiaoqiang zhao static void pl110_realize(DeviceState *dev, Error **errp)
544bdd5003aSpbrook {
5455d7a11e4SAndreas Färber PL110State *s = PL110(dev);
546caae8032Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
547bdd5003aSpbrook
548c2093660SPhilippe Mathieu-Daudé if (!s->fbmem) {
549c2093660SPhilippe Mathieu-Daudé error_setg(errp, "'framebuffer-memory' property was not set");
550c2093660SPhilippe Mathieu-Daudé return;
551c2093660SPhilippe Mathieu-Daudé }
552c2093660SPhilippe Mathieu-Daudé
5533eadad55SPaolo Bonzini memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
5545d7a11e4SAndreas Färber sysbus_init_mmio(sbd, &s->iomem);
5555d7a11e4SAndreas Färber sysbus_init_irq(sbd, &s->irq);
55624da047aSLinus Walleij s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
55724da047aSLinus Walleij pl110_vblank_interrupt, s);
5585d7a11e4SAndreas Färber qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
5595643706aSGerd Hoffmann s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
560bdd5003aSpbrook }
5612e9bdce5SPaul Brook
pl110_init(Object * obj)5625d7a11e4SAndreas Färber static void pl110_init(Object *obj)
5632e9bdce5SPaul Brook {
5645d7a11e4SAndreas Färber PL110State *s = PL110(obj);
5655d7a11e4SAndreas Färber
566c7bf3492SEduardo Habkost s->version = VERSION_PL110;
5674fbf5556SPeter Maydell }
5684fbf5556SPeter Maydell
pl110_versatile_init(Object * obj)5695d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj)
5704fbf5556SPeter Maydell {
5715d7a11e4SAndreas Färber PL110State *s = PL110(obj);
5725d7a11e4SAndreas Färber
573c7bf3492SEduardo Habkost s->version = VERSION_PL110_VERSATILE;
5745d7a11e4SAndreas Färber }
5755d7a11e4SAndreas Färber
pl111_init(Object * obj)5765d7a11e4SAndreas Färber static void pl111_init(Object *obj)
5775d7a11e4SAndreas Färber {
5785d7a11e4SAndreas Färber PL110State *s = PL110(obj);
5795d7a11e4SAndreas Färber
580c7bf3492SEduardo Habkost s->version = VERSION_PL111;
5812e9bdce5SPaul Brook }
5822e9bdce5SPaul Brook
pl110_class_init(ObjectClass * klass,const void * data)583*12d1a768SPhilippe Mathieu-Daudé static void pl110_class_init(ObjectClass *klass, const void *data)
584999e12bbSAnthony Liguori {
58539bffca2SAnthony Liguori DeviceClass *dc = DEVICE_CLASS(klass);
586999e12bbSAnthony Liguori
587125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
58839bffca2SAnthony Liguori dc->vmsd = &vmstate_pl110;
589caae8032Sxiaoqiang zhao dc->realize = pl110_realize;
590c2093660SPhilippe Mathieu-Daudé device_class_set_props(dc, pl110_properties);
591999e12bbSAnthony Liguori }
592999e12bbSAnthony Liguori
5938c43a6f0SAndreas Färber static const TypeInfo pl110_info = {
5945d7a11e4SAndreas Färber .name = TYPE_PL110,
59539bffca2SAnthony Liguori .parent = TYPE_SYS_BUS_DEVICE,
596513960eaSAndreas Färber .instance_size = sizeof(PL110State),
5975d7a11e4SAndreas Färber .instance_init = pl110_init,
598999e12bbSAnthony Liguori .class_init = pl110_class_init,
5998c60d065SPeter Maydell };
6008c60d065SPeter Maydell
6018c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = {
602999e12bbSAnthony Liguori .name = "pl110_versatile",
6035d7a11e4SAndreas Färber .parent = TYPE_PL110,
6045d7a11e4SAndreas Färber .instance_init = pl110_versatile_init,
6058c60d065SPeter Maydell };
6068c60d065SPeter Maydell
6078c43a6f0SAndreas Färber static const TypeInfo pl111_info = {
608999e12bbSAnthony Liguori .name = "pl111",
6095d7a11e4SAndreas Färber .parent = TYPE_PL110,
6105d7a11e4SAndreas Färber .instance_init = pl111_init,
6114fbf5556SPeter Maydell };
6124fbf5556SPeter Maydell
pl110_register_types(void)61383f7d43aSAndreas Färber static void pl110_register_types(void)
6142e9bdce5SPaul Brook {
61539bffca2SAnthony Liguori type_register_static(&pl110_info);
61639bffca2SAnthony Liguori type_register_static(&pl110_versatile_info);
61739bffca2SAnthony Liguori type_register_static(&pl111_info);
6182e9bdce5SPaul Brook }
6192e9bdce5SPaul Brook
62083f7d43aSAndreas Färber type_init(pl110_register_types)
621