xref: /qemu/hw/display/pl110.c (revision 560ebce6b2c54367e63757567230344c13999dc7)
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"
13d6454270SMarkus Armbruster #include "migration/vmstate.h"
1428ecbaeeSPaolo Bonzini #include "ui/console.h"
1547b43a1fSPaolo Bonzini #include "framebuffer.h"
1628ecbaeeSPaolo Bonzini #include "ui/pixel_ops.h"
1724da047aSLinus Walleij #include "qemu/timer.h"
1803dd024fSPaolo Bonzini #include "qemu/log.h"
190b8fa32fSMarkus Armbruster #include "qemu/module.h"
20db1015e9SEduardo Habkost #include "qom/object.h"
21bdd5003aSpbrook 
22bdd5003aSpbrook #define PL110_CR_EN   0x001
23e9c05b42Sbalrog #define PL110_CR_BGR  0x100
24bdd5003aSpbrook #define PL110_CR_BEBO 0x200
25bdd5003aSpbrook #define PL110_CR_BEPO 0x400
26bdd5003aSpbrook #define PL110_CR_PWR  0x800
2724da047aSLinus Walleij #define PL110_IE_NB   0x004
2824da047aSLinus Walleij #define PL110_IE_VC   0x008
29bdd5003aSpbrook 
30bdd5003aSpbrook enum pl110_bppmode
31bdd5003aSpbrook {
32bdd5003aSpbrook     BPP_1,
33bdd5003aSpbrook     BPP_2,
34bdd5003aSpbrook     BPP_4,
35bdd5003aSpbrook     BPP_8,
36bdd5003aSpbrook     BPP_16,
374fbf5556SPeter Maydell     BPP_32,
384fbf5556SPeter Maydell     BPP_16_565, /* PL111 only */
394fbf5556SPeter Maydell     BPP_12      /* PL111 only */
404fbf5556SPeter Maydell };
414fbf5556SPeter Maydell 
424fbf5556SPeter Maydell 
434fbf5556SPeter Maydell /* The Versatile/PB uses a slightly modified PL110 controller.  */
444fbf5556SPeter Maydell enum pl110_version
454fbf5556SPeter Maydell {
46c7bf3492SEduardo Habkost     VERSION_PL110,
47c7bf3492SEduardo Habkost     VERSION_PL110_VERSATILE,
48c7bf3492SEduardo Habkost     VERSION_PL111
49bdd5003aSpbrook };
50bdd5003aSpbrook 
515d7a11e4SAndreas Färber #define TYPE_PL110 "pl110"
528063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110)
535d7a11e4SAndreas Färber 
54db1015e9SEduardo Habkost struct PL110State {
555d7a11e4SAndreas Färber     SysBusDevice parent_obj;
565d7a11e4SAndreas Färber 
571a6b31ceSAvi Kivity     MemoryRegion iomem;
58c1076c3eSPaolo Bonzini     MemoryRegionSection fbsection;
59c78f7137SGerd Hoffmann     QemuConsole *con;
6024da047aSLinus Walleij     QEMUTimer *vblank_timer;
61c60e08d9Spbrook 
624fbf5556SPeter Maydell     int version;
63bdd5003aSpbrook     uint32_t timing[4];
64bdd5003aSpbrook     uint32_t cr;
65bdd5003aSpbrook     uint32_t upbase;
66bdd5003aSpbrook     uint32_t lpbase;
67bdd5003aSpbrook     uint32_t int_status;
68bdd5003aSpbrook     uint32_t int_mask;
69bdd5003aSpbrook     int cols;
70bdd5003aSpbrook     int rows;
71bdd5003aSpbrook     enum pl110_bppmode bpp;
72bdd5003aSpbrook     int invalidate;
73242ea2c6SPeter Maydell     uint32_t mux_ctrl;
746e4c0d1fSPeter Maydell     uint32_t palette[256];
756e4c0d1fSPeter Maydell     uint32_t raw_palette[128];
76d537cf6cSpbrook     qemu_irq irq;
77db1015e9SEduardo Habkost };
78bdd5003aSpbrook 
79128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id);
80128939a9SPeter Maydell 
818c60d065SPeter Maydell static const VMStateDescription vmstate_pl110 = {
828c60d065SPeter Maydell     .name = "pl110",
83242ea2c6SPeter Maydell     .version_id = 2,
848c60d065SPeter Maydell     .minimum_version_id = 1,
85128939a9SPeter Maydell     .post_load = vmstate_pl110_post_load,
868c60d065SPeter Maydell     .fields = (VMStateField[]) {
87513960eaSAndreas Färber         VMSTATE_INT32(version, PL110State),
88513960eaSAndreas Färber         VMSTATE_UINT32_ARRAY(timing, PL110State, 4),
89513960eaSAndreas Färber         VMSTATE_UINT32(cr, PL110State),
90513960eaSAndreas Färber         VMSTATE_UINT32(upbase, PL110State),
91513960eaSAndreas Färber         VMSTATE_UINT32(lpbase, PL110State),
92513960eaSAndreas Färber         VMSTATE_UINT32(int_status, PL110State),
93513960eaSAndreas Färber         VMSTATE_UINT32(int_mask, PL110State),
94513960eaSAndreas Färber         VMSTATE_INT32(cols, PL110State),
95513960eaSAndreas Färber         VMSTATE_INT32(rows, PL110State),
96513960eaSAndreas Färber         VMSTATE_UINT32(bpp, PL110State),
97513960eaSAndreas Färber         VMSTATE_INT32(invalidate, PL110State),
98513960eaSAndreas Färber         VMSTATE_UINT32_ARRAY(palette, PL110State, 256),
99513960eaSAndreas Färber         VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128),
100513960eaSAndreas Färber         VMSTATE_UINT32_V(mux_ctrl, PL110State, 2),
1018c60d065SPeter Maydell         VMSTATE_END_OF_LIST()
1028c60d065SPeter Maydell     }
1038c60d065SPeter Maydell };
1048c60d065SPeter Maydell 
105bdd5003aSpbrook static const unsigned char pl110_id[] =
106bdd5003aSpbrook { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
107bdd5003aSpbrook 
1084fbf5556SPeter Maydell static const unsigned char pl111_id[] = {
1094fbf5556SPeter Maydell     0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
1104fbf5556SPeter Maydell };
1114fbf5556SPeter Maydell 
112031c44e4SPeter Maydell 
1134fbf5556SPeter Maydell /* Indexed by pl110_version */
1144fbf5556SPeter Maydell static const unsigned char *idregs[] = {
1154fbf5556SPeter Maydell     pl110_id,
116031c44e4SPeter Maydell     /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board
117031c44e4SPeter Maydell      * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware
118031c44e4SPeter Maydell      * itself has the same ID values as a stock PL110, and guests (in
119031c44e4SPeter Maydell      * particular Linux) rely on this. We emulate what the hardware does,
120031c44e4SPeter Maydell      * rather than what the docs claim it ought to do.
121031c44e4SPeter Maydell      */
122031c44e4SPeter Maydell     pl110_id,
1234fbf5556SPeter Maydell     pl111_id
1244fbf5556SPeter Maydell };
1254fbf5556SPeter Maydell 
126bdd5003aSpbrook #define BITS 32
127*560ebce6SPeter Maydell #define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
128*560ebce6SPeter Maydell 
129*560ebce6SPeter Maydell #undef RGB
130*560ebce6SPeter Maydell #define BORDER bgr
131*560ebce6SPeter Maydell #define ORDER 0
13247b43a1fSPaolo Bonzini #include "pl110_template.h"
133*560ebce6SPeter Maydell #define ORDER 1
134*560ebce6SPeter Maydell #include "pl110_template.h"
135*560ebce6SPeter Maydell #define ORDER 2
136*560ebce6SPeter Maydell #include "pl110_template.h"
137*560ebce6SPeter Maydell #undef BORDER
138*560ebce6SPeter Maydell #define RGB
139*560ebce6SPeter Maydell #define BORDER rgb
140*560ebce6SPeter Maydell #define ORDER 0
141*560ebce6SPeter Maydell #include "pl110_template.h"
142*560ebce6SPeter Maydell #define ORDER 1
143*560ebce6SPeter Maydell #include "pl110_template.h"
144*560ebce6SPeter Maydell #define ORDER 2
145*560ebce6SPeter Maydell #include "pl110_template.h"
146*560ebce6SPeter Maydell #undef BORDER
147*560ebce6SPeter Maydell 
148*560ebce6SPeter Maydell static drawfn pl110_draw_fn_32[48] = {
149*560ebce6SPeter Maydell     pl110_draw_line1_lblp_bgr32,
150*560ebce6SPeter Maydell     pl110_draw_line2_lblp_bgr32,
151*560ebce6SPeter Maydell     pl110_draw_line4_lblp_bgr32,
152*560ebce6SPeter Maydell     pl110_draw_line8_lblp_bgr32,
153*560ebce6SPeter Maydell     pl110_draw_line16_555_lblp_bgr32,
154*560ebce6SPeter Maydell     pl110_draw_line32_lblp_bgr32,
155*560ebce6SPeter Maydell     pl110_draw_line16_lblp_bgr32,
156*560ebce6SPeter Maydell     pl110_draw_line12_lblp_bgr32,
157*560ebce6SPeter Maydell 
158*560ebce6SPeter Maydell     pl110_draw_line1_bbbp_bgr32,
159*560ebce6SPeter Maydell     pl110_draw_line2_bbbp_bgr32,
160*560ebce6SPeter Maydell     pl110_draw_line4_bbbp_bgr32,
161*560ebce6SPeter Maydell     pl110_draw_line8_bbbp_bgr32,
162*560ebce6SPeter Maydell     pl110_draw_line16_555_bbbp_bgr32,
163*560ebce6SPeter Maydell     pl110_draw_line32_bbbp_bgr32,
164*560ebce6SPeter Maydell     pl110_draw_line16_bbbp_bgr32,
165*560ebce6SPeter Maydell     pl110_draw_line12_bbbp_bgr32,
166*560ebce6SPeter Maydell 
167*560ebce6SPeter Maydell     pl110_draw_line1_lbbp_bgr32,
168*560ebce6SPeter Maydell     pl110_draw_line2_lbbp_bgr32,
169*560ebce6SPeter Maydell     pl110_draw_line4_lbbp_bgr32,
170*560ebce6SPeter Maydell     pl110_draw_line8_lbbp_bgr32,
171*560ebce6SPeter Maydell     pl110_draw_line16_555_lbbp_bgr32,
172*560ebce6SPeter Maydell     pl110_draw_line32_lbbp_bgr32,
173*560ebce6SPeter Maydell     pl110_draw_line16_lbbp_bgr32,
174*560ebce6SPeter Maydell     pl110_draw_line12_lbbp_bgr32,
175*560ebce6SPeter Maydell 
176*560ebce6SPeter Maydell     pl110_draw_line1_lblp_rgb32,
177*560ebce6SPeter Maydell     pl110_draw_line2_lblp_rgb32,
178*560ebce6SPeter Maydell     pl110_draw_line4_lblp_rgb32,
179*560ebce6SPeter Maydell     pl110_draw_line8_lblp_rgb32,
180*560ebce6SPeter Maydell     pl110_draw_line16_555_lblp_rgb32,
181*560ebce6SPeter Maydell     pl110_draw_line32_lblp_rgb32,
182*560ebce6SPeter Maydell     pl110_draw_line16_lblp_rgb32,
183*560ebce6SPeter Maydell     pl110_draw_line12_lblp_rgb32,
184*560ebce6SPeter Maydell 
185*560ebce6SPeter Maydell     pl110_draw_line1_bbbp_rgb32,
186*560ebce6SPeter Maydell     pl110_draw_line2_bbbp_rgb32,
187*560ebce6SPeter Maydell     pl110_draw_line4_bbbp_rgb32,
188*560ebce6SPeter Maydell     pl110_draw_line8_bbbp_rgb32,
189*560ebce6SPeter Maydell     pl110_draw_line16_555_bbbp_rgb32,
190*560ebce6SPeter Maydell     pl110_draw_line32_bbbp_rgb32,
191*560ebce6SPeter Maydell     pl110_draw_line16_bbbp_rgb32,
192*560ebce6SPeter Maydell     pl110_draw_line12_bbbp_rgb32,
193*560ebce6SPeter Maydell 
194*560ebce6SPeter Maydell     pl110_draw_line1_lbbp_rgb32,
195*560ebce6SPeter Maydell     pl110_draw_line2_lbbp_rgb32,
196*560ebce6SPeter Maydell     pl110_draw_line4_lbbp_rgb32,
197*560ebce6SPeter Maydell     pl110_draw_line8_lbbp_rgb32,
198*560ebce6SPeter Maydell     pl110_draw_line16_555_lbbp_rgb32,
199*560ebce6SPeter Maydell     pl110_draw_line32_lbbp_rgb32,
200*560ebce6SPeter Maydell     pl110_draw_line16_lbbp_rgb32,
201*560ebce6SPeter Maydell     pl110_draw_line12_lbbp_rgb32,
202*560ebce6SPeter Maydell };
203*560ebce6SPeter Maydell 
204*560ebce6SPeter Maydell #undef BITS
205*560ebce6SPeter Maydell #undef COPY_PIXEL
206*560ebce6SPeter 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;
2165d7a11e4SAndreas Färber     SysBusDevice *sbd;
217c78f7137SGerd Hoffmann     DisplaySurface *surface = qemu_console_surface(s->con);
218bdd5003aSpbrook     drawfn fn;
219bdd5003aSpbrook     int src_width;
220e9c05b42Sbalrog     int bpp_offset;
221714fa308Spbrook     int first;
222714fa308Spbrook     int last;
223bdd5003aSpbrook 
2245d7a11e4SAndreas Färber     if (!pl110_enabled(s)) {
225bdd5003aSpbrook         return;
2265d7a11e4SAndreas Färber     }
2275d7a11e4SAndreas Färber 
2285d7a11e4SAndreas Färber     sbd = SYS_BUS_DEVICE(s);
229bdd5003aSpbrook 
230e9c05b42Sbalrog     if (s->cr & PL110_CR_BGR)
231e9c05b42Sbalrog         bpp_offset = 0;
232bdd5003aSpbrook     else
2334fbf5556SPeter Maydell         bpp_offset = 24;
2344fbf5556SPeter Maydell 
235c7bf3492SEduardo Habkost     if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) {
2364fbf5556SPeter Maydell         /* The PL110's native 16 bit mode is 5551; however
2374fbf5556SPeter Maydell          * most boards with a PL110 implement an external
2384fbf5556SPeter Maydell          * mux which allows bits to be reshuffled to give
2394fbf5556SPeter Maydell          * 565 format. The mux is typically controlled by
2404fbf5556SPeter Maydell          * an external system register.
241242ea2c6SPeter Maydell          * This is controlled by a GPIO input pin
2424fbf5556SPeter Maydell          * so boards can wire it up to their register.
2434fbf5556SPeter Maydell          *
2444fbf5556SPeter Maydell          * The PL111 straightforwardly implements both
2454fbf5556SPeter Maydell          * 5551 and 565 under control of the bpp field
2464fbf5556SPeter Maydell          * in the LCDControl register.
2474fbf5556SPeter Maydell          */
248242ea2c6SPeter Maydell         switch (s->mux_ctrl) {
249242ea2c6SPeter Maydell         case 3: /* 565 BGR */
250242ea2c6SPeter Maydell             bpp_offset = (BPP_16_565 - BPP_16);
251242ea2c6SPeter Maydell             break;
252242ea2c6SPeter Maydell         case 1: /* 5551 */
253242ea2c6SPeter Maydell             break;
254242ea2c6SPeter Maydell         case 0: /* 888; also if we have loaded vmstate from an old version */
255242ea2c6SPeter Maydell         case 2: /* 565 RGB */
256242ea2c6SPeter Maydell         default:
257242ea2c6SPeter Maydell             /* treat as 565 but honour BGR bit */
2584fbf5556SPeter Maydell             bpp_offset += (BPP_16_565 - BPP_16);
259242ea2c6SPeter Maydell             break;
260242ea2c6SPeter Maydell         }
2614fbf5556SPeter Maydell     }
262e9c05b42Sbalrog 
26362bdc8c1SPeter Maydell     if (s->cr & PL110_CR_BEBO) {
26462bdc8c1SPeter Maydell         fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset];
26562bdc8c1SPeter Maydell     } else if (s->cr & PL110_CR_BEPO) {
26662bdc8c1SPeter Maydell         fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset];
26762bdc8c1SPeter Maydell     } else {
26862bdc8c1SPeter Maydell         fn = pl110_draw_fn_32[s->bpp + bpp_offset];
26962bdc8c1SPeter Maydell     }
270bdd5003aSpbrook 
271bdd5003aSpbrook     src_width = s->cols;
272bdd5003aSpbrook     switch (s->bpp) {
273bdd5003aSpbrook     case BPP_1:
274bdd5003aSpbrook         src_width >>= 3;
275bdd5003aSpbrook         break;
276bdd5003aSpbrook     case BPP_2:
277bdd5003aSpbrook         src_width >>= 2;
278bdd5003aSpbrook         break;
279bdd5003aSpbrook     case BPP_4:
280bdd5003aSpbrook         src_width >>= 1;
281bdd5003aSpbrook         break;
282bdd5003aSpbrook     case BPP_8:
283bdd5003aSpbrook         break;
284bdd5003aSpbrook     case BPP_16:
2854fbf5556SPeter Maydell     case BPP_16_565:
2864fbf5556SPeter Maydell     case BPP_12:
287bdd5003aSpbrook         src_width <<= 1;
288bdd5003aSpbrook         break;
289bdd5003aSpbrook     case BPP_32:
290bdd5003aSpbrook         src_width <<= 2;
291bdd5003aSpbrook         break;
292bdd5003aSpbrook     }
293714fa308Spbrook     first = 0;
294c1076c3eSPaolo Bonzini     if (s->invalidate) {
295c1076c3eSPaolo Bonzini         framebuffer_update_memory_section(&s->fbsection,
296c1076c3eSPaolo Bonzini                                           sysbus_address_space(sbd),
297c1076c3eSPaolo Bonzini                                           s->upbase,
298c1076c3eSPaolo Bonzini                                           s->rows, src_width);
299c1076c3eSPaolo Bonzini     }
300c1076c3eSPaolo Bonzini 
301c1076c3eSPaolo Bonzini     framebuffer_update_display(surface, &s->fbsection,
302c1076c3eSPaolo Bonzini                                s->cols, s->rows,
30362bdc8c1SPeter Maydell                                src_width, s->cols * 4, 0,
304714fa308Spbrook                                s->invalidate,
3056e4c0d1fSPeter Maydell                                fn, s->palette,
306714fa308Spbrook                                &first, &last);
307c1076c3eSPaolo Bonzini 
308714fa308Spbrook     if (first >= 0) {
309c78f7137SGerd Hoffmann         dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
310bdd5003aSpbrook     }
311714fa308Spbrook     s->invalidate = 0;
312714fa308Spbrook }
313bdd5003aSpbrook 
31495219897Spbrook static void pl110_invalidate_display(void * opaque)
315bdd5003aSpbrook {
316513960eaSAndreas Färber     PL110State *s = (PL110State *)opaque;
317bdd5003aSpbrook     s->invalidate = 1;
318bfdb3629SBlue Swirl     if (pl110_enabled(s)) {
319c78f7137SGerd Hoffmann         qemu_console_resize(s->con, s->cols, s->rows);
320bfdb3629SBlue Swirl     }
321bdd5003aSpbrook }
322bdd5003aSpbrook 
323513960eaSAndreas Färber static void pl110_update_palette(PL110State *s, int n)
324bdd5003aSpbrook {
325c78f7137SGerd Hoffmann     DisplaySurface *surface = qemu_console_surface(s->con);
326bdd5003aSpbrook     int i;
327bdd5003aSpbrook     uint32_t raw;
328bdd5003aSpbrook     unsigned int r, g, b;
329bdd5003aSpbrook 
3306e4c0d1fSPeter Maydell     raw = s->raw_palette[n];
331bdd5003aSpbrook     n <<= 1;
332bdd5003aSpbrook     for (i = 0; i < 2; i++) {
333bdd5003aSpbrook         r = (raw & 0x1f) << 3;
334bdd5003aSpbrook         raw >>= 5;
335bdd5003aSpbrook         g = (raw & 0x1f) << 3;
336bdd5003aSpbrook         raw >>= 5;
337bdd5003aSpbrook         b = (raw & 0x1f) << 3;
338bdd5003aSpbrook         /* The I bit is ignored.  */
339bdd5003aSpbrook         raw >>= 6;
340c78f7137SGerd Hoffmann         switch (surface_bits_per_pixel(surface)) {
341bdd5003aSpbrook         case 8:
3426e4c0d1fSPeter Maydell             s->palette[n] = rgb_to_pixel8(r, g, b);
343bdd5003aSpbrook             break;
344bdd5003aSpbrook         case 15:
3456e4c0d1fSPeter Maydell             s->palette[n] = rgb_to_pixel15(r, g, b);
346bdd5003aSpbrook             break;
347bdd5003aSpbrook         case 16:
3486e4c0d1fSPeter Maydell             s->palette[n] = rgb_to_pixel16(r, g, b);
349bdd5003aSpbrook             break;
350bdd5003aSpbrook         case 24:
351bdd5003aSpbrook         case 32:
3526e4c0d1fSPeter Maydell             s->palette[n] = rgb_to_pixel32(r, g, b);
353bdd5003aSpbrook             break;
354bdd5003aSpbrook         }
355bdd5003aSpbrook         n++;
356bdd5003aSpbrook     }
357bdd5003aSpbrook }
358bdd5003aSpbrook 
359513960eaSAndreas Färber static void pl110_resize(PL110State *s, int width, int height)
360bdd5003aSpbrook {
361bdd5003aSpbrook     if (width != s->cols || height != s->rows) {
362bdd5003aSpbrook         if (pl110_enabled(s)) {
363c78f7137SGerd Hoffmann             qemu_console_resize(s->con, width, height);
364bdd5003aSpbrook         }
365bdd5003aSpbrook     }
366bdd5003aSpbrook     s->cols = width;
367bdd5003aSpbrook     s->rows = height;
368bdd5003aSpbrook }
369bdd5003aSpbrook 
370bdd5003aSpbrook /* Update interrupts.  */
371513960eaSAndreas Färber static void pl110_update(PL110State *s)
372bdd5003aSpbrook {
37324da047aSLinus Walleij     /* Raise IRQ if enabled and any status bit is 1 */
37424da047aSLinus Walleij     if (s->int_status & s->int_mask) {
37524da047aSLinus Walleij         qemu_irq_raise(s->irq);
37624da047aSLinus Walleij     } else {
37724da047aSLinus Walleij         qemu_irq_lower(s->irq);
37824da047aSLinus Walleij     }
37924da047aSLinus Walleij }
38024da047aSLinus Walleij 
38124da047aSLinus Walleij static void pl110_vblank_interrupt(void *opaque)
38224da047aSLinus Walleij {
38324da047aSLinus Walleij     PL110State *s = opaque;
38424da047aSLinus Walleij 
38524da047aSLinus Walleij     /* Fire the vertical compare and next base IRQs and re-arm */
38624da047aSLinus Walleij     s->int_status |= (PL110_IE_NB | PL110_IE_VC);
38724da047aSLinus Walleij     timer_mod(s->vblank_timer,
38824da047aSLinus Walleij               qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
38924da047aSLinus Walleij                                 NANOSECONDS_PER_SECOND / 60);
39024da047aSLinus Walleij     pl110_update(s);
391bdd5003aSpbrook }
392bdd5003aSpbrook 
393a8170e5eSAvi Kivity static uint64_t pl110_read(void *opaque, hwaddr offset,
3941a6b31ceSAvi Kivity                            unsigned size)
395bdd5003aSpbrook {
396513960eaSAndreas Färber     PL110State *s = (PL110State *)opaque;
397bdd5003aSpbrook 
398bdd5003aSpbrook     if (offset >= 0xfe0 && offset < 0x1000) {
3994fbf5556SPeter Maydell         return idregs[s->version][(offset - 0xfe0) >> 2];
400bdd5003aSpbrook     }
401bdd5003aSpbrook     if (offset >= 0x200 && offset < 0x400) {
4026e4c0d1fSPeter Maydell         return s->raw_palette[(offset - 0x200) >> 2];
403bdd5003aSpbrook     }
404bdd5003aSpbrook     switch (offset >> 2) {
405bdd5003aSpbrook     case 0: /* LCDTiming0 */
406bdd5003aSpbrook         return s->timing[0];
407bdd5003aSpbrook     case 1: /* LCDTiming1 */
408bdd5003aSpbrook         return s->timing[1];
409bdd5003aSpbrook     case 2: /* LCDTiming2 */
410bdd5003aSpbrook         return s->timing[2];
411bdd5003aSpbrook     case 3: /* LCDTiming3 */
412bdd5003aSpbrook         return s->timing[3];
413bdd5003aSpbrook     case 4: /* LCDUPBASE */
414bdd5003aSpbrook         return s->upbase;
415bdd5003aSpbrook     case 5: /* LCDLPBASE */
416bdd5003aSpbrook         return s->lpbase;
417bdd5003aSpbrook     case 6: /* LCDIMSC */
418c7bf3492SEduardo Habkost         if (s->version != VERSION_PL110) {
41964075cd7Spbrook             return s->cr;
4204fbf5556SPeter Maydell         }
421bdd5003aSpbrook         return s->int_mask;
422bdd5003aSpbrook     case 7: /* LCDControl */
423c7bf3492SEduardo Habkost         if (s->version != VERSION_PL110) {
42464075cd7Spbrook             return s->int_mask;
4254fbf5556SPeter Maydell         }
426bdd5003aSpbrook         return s->cr;
427bdd5003aSpbrook     case 8: /* LCDRIS */
428bdd5003aSpbrook         return s->int_status;
429bdd5003aSpbrook     case 9: /* LCDMIS */
430bdd5003aSpbrook         return s->int_status & s->int_mask;
431bdd5003aSpbrook     case 11: /* LCDUPCURR */
432bdd5003aSpbrook         /* TODO: Implement vertical refresh.  */
433bdd5003aSpbrook         return s->upbase;
434bdd5003aSpbrook     case 12: /* LCDLPCURR */
435bdd5003aSpbrook         return s->lpbase;
436bdd5003aSpbrook     default:
437375cb560SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
438375cb560SPeter Maydell                       "pl110_read: Bad offset %x\n", (int)offset);
439bdd5003aSpbrook         return 0;
440bdd5003aSpbrook     }
441bdd5003aSpbrook }
442bdd5003aSpbrook 
443a8170e5eSAvi Kivity static void pl110_write(void *opaque, hwaddr offset,
4441a6b31ceSAvi Kivity                         uint64_t val, unsigned size)
445bdd5003aSpbrook {
446513960eaSAndreas Färber     PL110State *s = (PL110State *)opaque;
447bdd5003aSpbrook     int n;
448bdd5003aSpbrook 
449bdd5003aSpbrook     /* For simplicity invalidate the display whenever a control register
45066a0a2cbSDong Xu Wang        is written to.  */
451bdd5003aSpbrook     s->invalidate = 1;
452bdd5003aSpbrook     if (offset >= 0x200 && offset < 0x400) {
4536e4c0d1fSPeter Maydell         /* Palette.  */
454bdd5003aSpbrook         n = (offset - 0x200) >> 2;
4556e4c0d1fSPeter Maydell         s->raw_palette[(offset - 0x200) >> 2] = val;
4566e4c0d1fSPeter Maydell         pl110_update_palette(s, n);
457e10c2bfbSpbrook         return;
458bdd5003aSpbrook     }
459bdd5003aSpbrook     switch (offset >> 2) {
460bdd5003aSpbrook     case 0: /* LCDTiming0 */
461bdd5003aSpbrook         s->timing[0] = val;
462bdd5003aSpbrook         n = ((val & 0xfc) + 4) * 4;
463bdd5003aSpbrook         pl110_resize(s, n, s->rows);
464bdd5003aSpbrook         break;
465bdd5003aSpbrook     case 1: /* LCDTiming1 */
466bdd5003aSpbrook         s->timing[1] = val;
467bdd5003aSpbrook         n = (val & 0x3ff) + 1;
468bdd5003aSpbrook         pl110_resize(s, s->cols, n);
469bdd5003aSpbrook         break;
470bdd5003aSpbrook     case 2: /* LCDTiming2 */
471bdd5003aSpbrook         s->timing[2] = val;
472bdd5003aSpbrook         break;
473bdd5003aSpbrook     case 3: /* LCDTiming3 */
474bdd5003aSpbrook         s->timing[3] = val;
475bdd5003aSpbrook         break;
476bdd5003aSpbrook     case 4: /* LCDUPBASE */
477bdd5003aSpbrook         s->upbase = val;
478bdd5003aSpbrook         break;
479bdd5003aSpbrook     case 5: /* LCDLPBASE */
480bdd5003aSpbrook         s->lpbase = val;
481bdd5003aSpbrook         break;
482bdd5003aSpbrook     case 6: /* LCDIMSC */
483c7bf3492SEduardo Habkost         if (s->version != VERSION_PL110) {
484cdbdb648Spbrook             goto control;
4854fbf5556SPeter Maydell         }
486cdbdb648Spbrook     imsc:
487bdd5003aSpbrook         s->int_mask = val;
488bdd5003aSpbrook         pl110_update(s);
489bdd5003aSpbrook         break;
490bdd5003aSpbrook     case 7: /* LCDControl */
491c7bf3492SEduardo Habkost         if (s->version != VERSION_PL110) {
492cdbdb648Spbrook             goto imsc;
4934fbf5556SPeter Maydell         }
494cdbdb648Spbrook     control:
495bdd5003aSpbrook         s->cr = val;
496bdd5003aSpbrook         s->bpp = (val >> 1) & 7;
497bdd5003aSpbrook         if (pl110_enabled(s)) {
498c78f7137SGerd Hoffmann             qemu_console_resize(s->con, s->cols, s->rows);
49924da047aSLinus Walleij             timer_mod(s->vblank_timer,
50024da047aSLinus Walleij                       qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
50124da047aSLinus Walleij                                         NANOSECONDS_PER_SECOND / 60);
50224da047aSLinus Walleij         } else {
50324da047aSLinus Walleij             timer_del(s->vblank_timer);
504bdd5003aSpbrook         }
505bdd5003aSpbrook         break;
506bdd5003aSpbrook     case 10: /* LCDICR */
507bdd5003aSpbrook         s->int_status &= ~val;
508bdd5003aSpbrook         pl110_update(s);
509bdd5003aSpbrook         break;
510bdd5003aSpbrook     default:
511375cb560SPeter Maydell         qemu_log_mask(LOG_GUEST_ERROR,
512375cb560SPeter Maydell                       "pl110_write: Bad offset %x\n", (int)offset);
513bdd5003aSpbrook     }
514bdd5003aSpbrook }
515bdd5003aSpbrook 
5161a6b31ceSAvi Kivity static const MemoryRegionOps pl110_ops = {
5171a6b31ceSAvi Kivity     .read = pl110_read,
5181a6b31ceSAvi Kivity     .write = pl110_write,
5191a6b31ceSAvi Kivity     .endianness = DEVICE_NATIVE_ENDIAN,
520bdd5003aSpbrook };
521bdd5003aSpbrook 
522242ea2c6SPeter Maydell static void pl110_mux_ctrl_set(void *opaque, int line, int level)
523242ea2c6SPeter Maydell {
524513960eaSAndreas Färber     PL110State *s = (PL110State *)opaque;
525242ea2c6SPeter Maydell     s->mux_ctrl = level;
526242ea2c6SPeter Maydell }
527242ea2c6SPeter Maydell 
528128939a9SPeter Maydell static int vmstate_pl110_post_load(void *opaque, int version_id)
529128939a9SPeter Maydell {
530513960eaSAndreas Färber     PL110State *s = opaque;
531128939a9SPeter Maydell     /* Make sure we redraw, and at the right size */
532128939a9SPeter Maydell     pl110_invalidate_display(s);
533128939a9SPeter Maydell     return 0;
534128939a9SPeter Maydell }
535128939a9SPeter Maydell 
536380cd056SGerd Hoffmann static const GraphicHwOps pl110_gfx_ops = {
537380cd056SGerd Hoffmann     .invalidate  = pl110_invalidate_display,
538380cd056SGerd Hoffmann     .gfx_update  = pl110_update_display,
539380cd056SGerd Hoffmann };
540380cd056SGerd Hoffmann 
541caae8032Sxiaoqiang zhao static void pl110_realize(DeviceState *dev, Error **errp)
542bdd5003aSpbrook {
5435d7a11e4SAndreas Färber     PL110State *s = PL110(dev);
544caae8032Sxiaoqiang zhao     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
545bdd5003aSpbrook 
5463eadad55SPaolo Bonzini     memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
5475d7a11e4SAndreas Färber     sysbus_init_mmio(sbd, &s->iomem);
5485d7a11e4SAndreas Färber     sysbus_init_irq(sbd, &s->irq);
54924da047aSLinus Walleij     s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
55024da047aSLinus Walleij                                    pl110_vblank_interrupt, s);
5515d7a11e4SAndreas Färber     qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
5525643706aSGerd Hoffmann     s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
553bdd5003aSpbrook }
5542e9bdce5SPaul Brook 
5555d7a11e4SAndreas Färber static void pl110_init(Object *obj)
5562e9bdce5SPaul Brook {
5575d7a11e4SAndreas Färber     PL110State *s = PL110(obj);
5585d7a11e4SAndreas Färber 
559c7bf3492SEduardo Habkost     s->version = VERSION_PL110;
5604fbf5556SPeter Maydell }
5614fbf5556SPeter Maydell 
5625d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj)
5634fbf5556SPeter Maydell {
5645d7a11e4SAndreas Färber     PL110State *s = PL110(obj);
5655d7a11e4SAndreas Färber 
566c7bf3492SEduardo Habkost     s->version = VERSION_PL110_VERSATILE;
5675d7a11e4SAndreas Färber }
5685d7a11e4SAndreas Färber 
5695d7a11e4SAndreas Färber static void pl111_init(Object *obj)
5705d7a11e4SAndreas Färber {
5715d7a11e4SAndreas Färber     PL110State *s = PL110(obj);
5725d7a11e4SAndreas Färber 
573c7bf3492SEduardo Habkost     s->version = VERSION_PL111;
5742e9bdce5SPaul Brook }
5752e9bdce5SPaul Brook 
576999e12bbSAnthony Liguori static void pl110_class_init(ObjectClass *klass, void *data)
577999e12bbSAnthony Liguori {
57839bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
579999e12bbSAnthony Liguori 
580125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
58139bffca2SAnthony Liguori     dc->vmsd = &vmstate_pl110;
582caae8032Sxiaoqiang zhao     dc->realize = pl110_realize;
583999e12bbSAnthony Liguori }
584999e12bbSAnthony Liguori 
5858c43a6f0SAndreas Färber static const TypeInfo pl110_info = {
5865d7a11e4SAndreas Färber     .name          = TYPE_PL110,
58739bffca2SAnthony Liguori     .parent        = TYPE_SYS_BUS_DEVICE,
588513960eaSAndreas Färber     .instance_size = sizeof(PL110State),
5895d7a11e4SAndreas Färber     .instance_init = pl110_init,
590999e12bbSAnthony Liguori     .class_init    = pl110_class_init,
5918c60d065SPeter Maydell };
5928c60d065SPeter Maydell 
5938c43a6f0SAndreas Färber static const TypeInfo pl110_versatile_info = {
594999e12bbSAnthony Liguori     .name          = "pl110_versatile",
5955d7a11e4SAndreas Färber     .parent        = TYPE_PL110,
5965d7a11e4SAndreas Färber     .instance_init = pl110_versatile_init,
5978c60d065SPeter Maydell };
5988c60d065SPeter Maydell 
5998c43a6f0SAndreas Färber static const TypeInfo pl111_info = {
600999e12bbSAnthony Liguori     .name          = "pl111",
6015d7a11e4SAndreas Färber     .parent        = TYPE_PL110,
6025d7a11e4SAndreas Färber     .instance_init = pl111_init,
6034fbf5556SPeter Maydell };
6044fbf5556SPeter Maydell 
60583f7d43aSAndreas Färber static void pl110_register_types(void)
6062e9bdce5SPaul Brook {
60739bffca2SAnthony Liguori     type_register_static(&pl110_info);
60839bffca2SAnthony Liguori     type_register_static(&pl110_versatile_info);
60939bffca2SAnthony Liguori     type_register_static(&pl111_info);
6102e9bdce5SPaul Brook }
6112e9bdce5SPaul Brook 
61283f7d43aSAndreas Färber type_init(pl110_register_types)
613