149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * Arm PrimeCell PL110 Color LCD Controller
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * Copyright (c) 2005-2009 CodeSourcery.
549ab747fSPaolo Bonzini * Written by Paul Brook
649ab747fSPaolo Bonzini *
749ab747fSPaolo Bonzini * This code is licensed under the GNU LGPL
849ab747fSPaolo Bonzini */
949ab747fSPaolo Bonzini
108ef94f0bSPeter Maydell #include "qemu/osdep.h"
1164552b6bSMarkus Armbruster #include "hw/irq.h"
1249ab747fSPaolo Bonzini #include "hw/sysbus.h"
13*c2093660SPhilippe Mathieu-Daudé #include "hw/qdev-properties.h"
14d6454270SMarkus Armbruster #include "migration/vmstate.h"
1549ab747fSPaolo Bonzini #include "ui/console.h"
1647b43a1fSPaolo Bonzini #include "framebuffer.h"
1749ab747fSPaolo 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"
2349ab747fSPaolo Bonzini
2449ab747fSPaolo Bonzini #define PL110_CR_EN 0x001
2549ab747fSPaolo Bonzini #define PL110_CR_BGR 0x100
2649ab747fSPaolo Bonzini #define PL110_CR_BEBO 0x200
2749ab747fSPaolo Bonzini #define PL110_CR_BEPO 0x400
2849ab747fSPaolo Bonzini #define PL110_CR_PWR 0x800
2924da047aSLinus Walleij #define PL110_IE_NB 0x004
3024da047aSLinus Walleij #define PL110_IE_VC 0x008
3149ab747fSPaolo Bonzini
3249ab747fSPaolo Bonzini enum pl110_bppmode
3349ab747fSPaolo Bonzini {
3449ab747fSPaolo Bonzini BPP_1,
3549ab747fSPaolo Bonzini BPP_2,
3649ab747fSPaolo Bonzini BPP_4,
3749ab747fSPaolo Bonzini BPP_8,
3849ab747fSPaolo Bonzini BPP_16,
3949ab747fSPaolo Bonzini BPP_32,
4049ab747fSPaolo Bonzini BPP_16_565, /* PL111 only */
4149ab747fSPaolo Bonzini BPP_12 /* PL111 only */
4249ab747fSPaolo Bonzini };
4349ab747fSPaolo Bonzini
4449ab747fSPaolo Bonzini
4549ab747fSPaolo Bonzini /* The Versatile/PB uses a slightly modified PL110 controller. */
4649ab747fSPaolo Bonzini enum pl110_version
4749ab747fSPaolo Bonzini {
48c7bf3492SEduardo Habkost VERSION_PL110,
49c7bf3492SEduardo Habkost VERSION_PL110_VERSATILE,
50c7bf3492SEduardo Habkost VERSION_PL111
5149ab747fSPaolo Bonzini };
5249ab747fSPaolo Bonzini
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
5949ab747fSPaolo Bonzini MemoryRegion iomem;
60c1076c3eSPaolo Bonzini MemoryRegionSection fbsection;
6149ab747fSPaolo Bonzini QemuConsole *con;
6224da047aSLinus Walleij QEMUTimer *vblank_timer;
6349ab747fSPaolo Bonzini
6449ab747fSPaolo Bonzini int version;
6549ab747fSPaolo Bonzini uint32_t timing[4];
6649ab747fSPaolo Bonzini uint32_t cr;
6749ab747fSPaolo Bonzini uint32_t upbase;
6849ab747fSPaolo Bonzini uint32_t lpbase;
6949ab747fSPaolo Bonzini uint32_t int_status;
7049ab747fSPaolo Bonzini uint32_t int_mask;
7149ab747fSPaolo Bonzini int cols;
7249ab747fSPaolo Bonzini int rows;
7349ab747fSPaolo Bonzini enum pl110_bppmode bpp;
7449ab747fSPaolo Bonzini int invalidate;
7549ab747fSPaolo Bonzini uint32_t mux_ctrl;
7649ab747fSPaolo Bonzini uint32_t palette[256];
7749ab747fSPaolo Bonzini uint32_t raw_palette[128];
7849ab747fSPaolo Bonzini qemu_irq irq;
79*c2093660SPhilippe Mathieu-Daudé MemoryRegion *fbmem;
80db1015e9SEduardo Habkost };
8149ab747fSPaolo Bonzini
8249ab747fSPaolo Bonzini static int vmstate_pl110_post_load(void *opaque, int version_id);
8349ab747fSPaolo Bonzini
8449ab747fSPaolo Bonzini static const VMStateDescription vmstate_pl110 = {
8549ab747fSPaolo Bonzini .name = "pl110",
8649ab747fSPaolo Bonzini .version_id = 2,
8749ab747fSPaolo Bonzini .minimum_version_id = 1,
8849ab747fSPaolo Bonzini .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),
10449ab747fSPaolo Bonzini VMSTATE_END_OF_LIST()
10549ab747fSPaolo Bonzini }
10649ab747fSPaolo Bonzini };
10749ab747fSPaolo Bonzini
10849ab747fSPaolo Bonzini static const unsigned char pl110_id[] =
10949ab747fSPaolo Bonzini { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
11049ab747fSPaolo Bonzini
11149ab747fSPaolo Bonzini static const unsigned char pl111_id[] = {
11249ab747fSPaolo Bonzini 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
11349ab747fSPaolo Bonzini };
11449ab747fSPaolo Bonzini
115031c44e4SPeter Maydell
11649ab747fSPaolo Bonzini /* Indexed by pl110_version */
11749ab747fSPaolo Bonzini static const unsigned char *idregs[] = {
11849ab747fSPaolo Bonzini 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,
12649ab747fSPaolo Bonzini pl111_id
12749ab747fSPaolo Bonzini };
12849ab747fSPaolo Bonzini
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 };
20749ab747fSPaolo Bonzini
pl110_enabled(PL110State * s)208513960eaSAndreas Färber static int pl110_enabled(PL110State *s)
20949ab747fSPaolo Bonzini {
21049ab747fSPaolo Bonzini return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
21149ab747fSPaolo Bonzini }
21249ab747fSPaolo Bonzini
pl110_update_display(void * opaque)21349ab747fSPaolo Bonzini static void pl110_update_display(void *opaque)
21449ab747fSPaolo Bonzini {
215513960eaSAndreas Färber PL110State *s = (PL110State *)opaque;
21649ab747fSPaolo Bonzini DisplaySurface *surface = qemu_console_surface(s->con);
21749ab747fSPaolo Bonzini drawfn fn;
21849ab747fSPaolo Bonzini int src_width;
21949ab747fSPaolo Bonzini int bpp_offset;
22049ab747fSPaolo Bonzini int first;
22149ab747fSPaolo Bonzini int last;
22249ab747fSPaolo Bonzini
2235d7a11e4SAndreas Färber if (!pl110_enabled(s)) {
22449ab747fSPaolo Bonzini return;
2255d7a11e4SAndreas Färber }
2265d7a11e4SAndreas Färber
22749ab747fSPaolo Bonzini if (s->cr & PL110_CR_BGR)
22849ab747fSPaolo Bonzini bpp_offset = 0;
22949ab747fSPaolo Bonzini else
23049ab747fSPaolo Bonzini bpp_offset = 24;
23149ab747fSPaolo Bonzini
232c7bf3492SEduardo Habkost if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) {
23349ab747fSPaolo Bonzini /* The PL110's native 16 bit mode is 5551; however
23449ab747fSPaolo Bonzini * most boards with a PL110 implement an external
23549ab747fSPaolo Bonzini * mux which allows bits to be reshuffled to give
23649ab747fSPaolo Bonzini * 565 format. The mux is typically controlled by
23749ab747fSPaolo Bonzini * an external system register.
23849ab747fSPaolo Bonzini * This is controlled by a GPIO input pin
23949ab747fSPaolo Bonzini * so boards can wire it up to their register.
24049ab747fSPaolo Bonzini *
24149ab747fSPaolo Bonzini * The PL111 straightforwardly implements both
24249ab747fSPaolo Bonzini * 5551 and 565 under control of the bpp field
24349ab747fSPaolo Bonzini * in the LCDControl register.
24449ab747fSPaolo Bonzini */
24549ab747fSPaolo Bonzini switch (s->mux_ctrl) {
24649ab747fSPaolo Bonzini case 3: /* 565 BGR */
24749ab747fSPaolo Bonzini bpp_offset = (BPP_16_565 - BPP_16);
24849ab747fSPaolo Bonzini break;
24949ab747fSPaolo Bonzini case 1: /* 5551 */
25049ab747fSPaolo Bonzini break;
25149ab747fSPaolo Bonzini case 0: /* 888; also if we have loaded vmstate from an old version */
25249ab747fSPaolo Bonzini case 2: /* 565 RGB */
25349ab747fSPaolo Bonzini default:
25449ab747fSPaolo Bonzini /* treat as 565 but honour BGR bit */
25549ab747fSPaolo Bonzini bpp_offset += (BPP_16_565 - BPP_16);
25649ab747fSPaolo Bonzini break;
25749ab747fSPaolo Bonzini }
25849ab747fSPaolo Bonzini }
25949ab747fSPaolo Bonzini
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 }
26749ab747fSPaolo Bonzini
26849ab747fSPaolo Bonzini src_width = s->cols;
26949ab747fSPaolo Bonzini switch (s->bpp) {
27049ab747fSPaolo Bonzini case BPP_1:
27149ab747fSPaolo Bonzini src_width >>= 3;
27249ab747fSPaolo Bonzini break;
27349ab747fSPaolo Bonzini case BPP_2:
27449ab747fSPaolo Bonzini src_width >>= 2;
27549ab747fSPaolo Bonzini break;
27649ab747fSPaolo Bonzini case BPP_4:
27749ab747fSPaolo Bonzini src_width >>= 1;
27849ab747fSPaolo Bonzini break;
27949ab747fSPaolo Bonzini case BPP_8:
28049ab747fSPaolo Bonzini break;
28149ab747fSPaolo Bonzini case BPP_16:
28249ab747fSPaolo Bonzini case BPP_16_565:
28349ab747fSPaolo Bonzini case BPP_12:
28449ab747fSPaolo Bonzini src_width <<= 1;
28549ab747fSPaolo Bonzini break;
28649ab747fSPaolo Bonzini case BPP_32:
28749ab747fSPaolo Bonzini src_width <<= 2;
28849ab747fSPaolo Bonzini break;
28949ab747fSPaolo Bonzini }
29049ab747fSPaolo Bonzini 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,
30149ab747fSPaolo Bonzini s->invalidate,
30249ab747fSPaolo Bonzini fn, s->palette,
30349ab747fSPaolo Bonzini &first, &last);
304c1076c3eSPaolo Bonzini
30549ab747fSPaolo Bonzini if (first >= 0) {
30649ab747fSPaolo Bonzini dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
30749ab747fSPaolo Bonzini }
30849ab747fSPaolo Bonzini s->invalidate = 0;
30949ab747fSPaolo Bonzini }
31049ab747fSPaolo Bonzini
pl110_invalidate_display(void * opaque)31149ab747fSPaolo Bonzini static void pl110_invalidate_display(void * opaque)
31249ab747fSPaolo Bonzini {
313513960eaSAndreas Färber PL110State *s = (PL110State *)opaque;
31449ab747fSPaolo Bonzini s->invalidate = 1;
31549ab747fSPaolo Bonzini if (pl110_enabled(s)) {
31649ab747fSPaolo Bonzini qemu_console_resize(s->con, s->cols, s->rows);
31749ab747fSPaolo Bonzini }
31849ab747fSPaolo Bonzini }
31949ab747fSPaolo Bonzini
pl110_update_palette(PL110State * s,int n)320513960eaSAndreas Färber static void pl110_update_palette(PL110State *s, int n)
32149ab747fSPaolo Bonzini {
32249ab747fSPaolo Bonzini DisplaySurface *surface = qemu_console_surface(s->con);
32349ab747fSPaolo Bonzini int i;
32449ab747fSPaolo Bonzini uint32_t raw;
32549ab747fSPaolo Bonzini unsigned int r, g, b;
32649ab747fSPaolo Bonzini
32749ab747fSPaolo Bonzini raw = s->raw_palette[n];
32849ab747fSPaolo Bonzini n <<= 1;
32949ab747fSPaolo Bonzini for (i = 0; i < 2; i++) {
33049ab747fSPaolo Bonzini r = (raw & 0x1f) << 3;
33149ab747fSPaolo Bonzini raw >>= 5;
33249ab747fSPaolo Bonzini g = (raw & 0x1f) << 3;
33349ab747fSPaolo Bonzini raw >>= 5;
33449ab747fSPaolo Bonzini b = (raw & 0x1f) << 3;
33549ab747fSPaolo Bonzini /* The I bit is ignored. */
33649ab747fSPaolo Bonzini raw >>= 6;
33749ab747fSPaolo Bonzini switch (surface_bits_per_pixel(surface)) {
33849ab747fSPaolo Bonzini case 8:
33949ab747fSPaolo Bonzini s->palette[n] = rgb_to_pixel8(r, g, b);
34049ab747fSPaolo Bonzini break;
34149ab747fSPaolo Bonzini case 15:
34249ab747fSPaolo Bonzini s->palette[n] = rgb_to_pixel15(r, g, b);
34349ab747fSPaolo Bonzini break;
34449ab747fSPaolo Bonzini case 16:
34549ab747fSPaolo Bonzini s->palette[n] = rgb_to_pixel16(r, g, b);
34649ab747fSPaolo Bonzini break;
34749ab747fSPaolo Bonzini case 24:
34849ab747fSPaolo Bonzini case 32:
34949ab747fSPaolo Bonzini s->palette[n] = rgb_to_pixel32(r, g, b);
35049ab747fSPaolo Bonzini break;
35149ab747fSPaolo Bonzini }
35249ab747fSPaolo Bonzini n++;
35349ab747fSPaolo Bonzini }
35449ab747fSPaolo Bonzini }
35549ab747fSPaolo Bonzini
pl110_resize(PL110State * s,int width,int height)356513960eaSAndreas Färber static void pl110_resize(PL110State *s, int width, int height)
35749ab747fSPaolo Bonzini {
35849ab747fSPaolo Bonzini if (width != s->cols || height != s->rows) {
35949ab747fSPaolo Bonzini if (pl110_enabled(s)) {
36049ab747fSPaolo Bonzini qemu_console_resize(s->con, width, height);
36149ab747fSPaolo Bonzini }
36249ab747fSPaolo Bonzini }
36349ab747fSPaolo Bonzini s->cols = width;
36449ab747fSPaolo Bonzini s->rows = height;
36549ab747fSPaolo Bonzini }
36649ab747fSPaolo Bonzini
36749ab747fSPaolo Bonzini /* Update interrupts. */
pl110_update(PL110State * s)368513960eaSAndreas Färber static void pl110_update(PL110State *s)
36949ab747fSPaolo Bonzini {
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);
38849ab747fSPaolo Bonzini }
38949ab747fSPaolo Bonzini
pl110_read(void * opaque,hwaddr offset,unsigned size)39049ab747fSPaolo Bonzini static uint64_t pl110_read(void *opaque, hwaddr offset,
39149ab747fSPaolo Bonzini unsigned size)
39249ab747fSPaolo Bonzini {
393513960eaSAndreas Färber PL110State *s = (PL110State *)opaque;
39449ab747fSPaolo Bonzini
39549ab747fSPaolo Bonzini if (offset >= 0xfe0 && offset < 0x1000) {
39649ab747fSPaolo Bonzini return idregs[s->version][(offset - 0xfe0) >> 2];
39749ab747fSPaolo Bonzini }
39849ab747fSPaolo Bonzini if (offset >= 0x200 && offset < 0x400) {
39949ab747fSPaolo Bonzini return s->raw_palette[(offset - 0x200) >> 2];
40049ab747fSPaolo Bonzini }
40149ab747fSPaolo Bonzini switch (offset >> 2) {
40249ab747fSPaolo Bonzini case 0: /* LCDTiming0 */
40349ab747fSPaolo Bonzini return s->timing[0];
40449ab747fSPaolo Bonzini case 1: /* LCDTiming1 */
40549ab747fSPaolo Bonzini return s->timing[1];
40649ab747fSPaolo Bonzini case 2: /* LCDTiming2 */
40749ab747fSPaolo Bonzini return s->timing[2];
40849ab747fSPaolo Bonzini case 3: /* LCDTiming3 */
40949ab747fSPaolo Bonzini return s->timing[3];
41049ab747fSPaolo Bonzini case 4: /* LCDUPBASE */
41149ab747fSPaolo Bonzini return s->upbase;
41249ab747fSPaolo Bonzini case 5: /* LCDLPBASE */
41349ab747fSPaolo Bonzini return s->lpbase;
41449ab747fSPaolo Bonzini case 6: /* LCDIMSC */
415c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) {
41649ab747fSPaolo Bonzini return s->cr;
41749ab747fSPaolo Bonzini }
41849ab747fSPaolo Bonzini return s->int_mask;
41949ab747fSPaolo Bonzini case 7: /* LCDControl */
420c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) {
42149ab747fSPaolo Bonzini return s->int_mask;
42249ab747fSPaolo Bonzini }
42349ab747fSPaolo Bonzini return s->cr;
42449ab747fSPaolo Bonzini case 8: /* LCDRIS */
42549ab747fSPaolo Bonzini return s->int_status;
42649ab747fSPaolo Bonzini case 9: /* LCDMIS */
42749ab747fSPaolo Bonzini return s->int_status & s->int_mask;
42849ab747fSPaolo Bonzini case 11: /* LCDUPCURR */
42949ab747fSPaolo Bonzini /* TODO: Implement vertical refresh. */
43049ab747fSPaolo Bonzini return s->upbase;
43149ab747fSPaolo Bonzini case 12: /* LCDLPCURR */
43249ab747fSPaolo Bonzini return s->lpbase;
43349ab747fSPaolo Bonzini default:
43449ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR,
43549ab747fSPaolo Bonzini "pl110_read: Bad offset %x\n", (int)offset);
43649ab747fSPaolo Bonzini return 0;
43749ab747fSPaolo Bonzini }
43849ab747fSPaolo Bonzini }
43949ab747fSPaolo Bonzini
pl110_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)44049ab747fSPaolo Bonzini static void pl110_write(void *opaque, hwaddr offset,
44149ab747fSPaolo Bonzini uint64_t val, unsigned size)
44249ab747fSPaolo Bonzini {
443513960eaSAndreas Färber PL110State *s = (PL110State *)opaque;
44449ab747fSPaolo Bonzini int n;
44549ab747fSPaolo Bonzini
44649ab747fSPaolo Bonzini /* For simplicity invalidate the display whenever a control register
44749ab747fSPaolo Bonzini is written to. */
44849ab747fSPaolo Bonzini s->invalidate = 1;
44949ab747fSPaolo Bonzini if (offset >= 0x200 && offset < 0x400) {
45049ab747fSPaolo Bonzini /* Palette. */
45149ab747fSPaolo Bonzini n = (offset - 0x200) >> 2;
45249ab747fSPaolo Bonzini s->raw_palette[(offset - 0x200) >> 2] = val;
45349ab747fSPaolo Bonzini pl110_update_palette(s, n);
45449ab747fSPaolo Bonzini return;
45549ab747fSPaolo Bonzini }
45649ab747fSPaolo Bonzini switch (offset >> 2) {
45749ab747fSPaolo Bonzini case 0: /* LCDTiming0 */
45849ab747fSPaolo Bonzini s->timing[0] = val;
45949ab747fSPaolo Bonzini n = ((val & 0xfc) + 4) * 4;
46049ab747fSPaolo Bonzini pl110_resize(s, n, s->rows);
46149ab747fSPaolo Bonzini break;
46249ab747fSPaolo Bonzini case 1: /* LCDTiming1 */
46349ab747fSPaolo Bonzini s->timing[1] = val;
46449ab747fSPaolo Bonzini n = (val & 0x3ff) + 1;
46549ab747fSPaolo Bonzini pl110_resize(s, s->cols, n);
46649ab747fSPaolo Bonzini break;
46749ab747fSPaolo Bonzini case 2: /* LCDTiming2 */
46849ab747fSPaolo Bonzini s->timing[2] = val;
46949ab747fSPaolo Bonzini break;
47049ab747fSPaolo Bonzini case 3: /* LCDTiming3 */
47149ab747fSPaolo Bonzini s->timing[3] = val;
47249ab747fSPaolo Bonzini break;
47349ab747fSPaolo Bonzini case 4: /* LCDUPBASE */
47449ab747fSPaolo Bonzini s->upbase = val;
47549ab747fSPaolo Bonzini break;
47649ab747fSPaolo Bonzini case 5: /* LCDLPBASE */
47749ab747fSPaolo Bonzini s->lpbase = val;
47849ab747fSPaolo Bonzini break;
47949ab747fSPaolo Bonzini case 6: /* LCDIMSC */
480c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) {
48149ab747fSPaolo Bonzini goto control;
48249ab747fSPaolo Bonzini }
48349ab747fSPaolo Bonzini imsc:
48449ab747fSPaolo Bonzini s->int_mask = val;
48549ab747fSPaolo Bonzini pl110_update(s);
48649ab747fSPaolo Bonzini break;
48749ab747fSPaolo Bonzini case 7: /* LCDControl */
488c7bf3492SEduardo Habkost if (s->version != VERSION_PL110) {
48949ab747fSPaolo Bonzini goto imsc;
49049ab747fSPaolo Bonzini }
49149ab747fSPaolo Bonzini control:
49249ab747fSPaolo Bonzini s->cr = val;
49349ab747fSPaolo Bonzini s->bpp = (val >> 1) & 7;
49449ab747fSPaolo Bonzini if (pl110_enabled(s)) {
49549ab747fSPaolo Bonzini 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);
50149ab747fSPaolo Bonzini }
50249ab747fSPaolo Bonzini break;
50349ab747fSPaolo Bonzini case 10: /* LCDICR */
50449ab747fSPaolo Bonzini s->int_status &= ~val;
50549ab747fSPaolo Bonzini pl110_update(s);
50649ab747fSPaolo Bonzini break;
50749ab747fSPaolo Bonzini default:
50849ab747fSPaolo Bonzini qemu_log_mask(LOG_GUEST_ERROR,
50949ab747fSPaolo Bonzini "pl110_write: Bad offset %x\n", (int)offset);
51049ab747fSPaolo Bonzini }
51149ab747fSPaolo Bonzini }
51249ab747fSPaolo Bonzini
51349ab747fSPaolo Bonzini static const MemoryRegionOps pl110_ops = {
51449ab747fSPaolo Bonzini .read = pl110_read,
51549ab747fSPaolo Bonzini .write = pl110_write,
51649ab747fSPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN,
51749ab747fSPaolo Bonzini };
51849ab747fSPaolo Bonzini
pl110_mux_ctrl_set(void * opaque,int line,int level)51949ab747fSPaolo Bonzini static void pl110_mux_ctrl_set(void *opaque, int line, int level)
52049ab747fSPaolo Bonzini {
521513960eaSAndreas Färber PL110State *s = (PL110State *)opaque;
52249ab747fSPaolo Bonzini s->mux_ctrl = level;
52349ab747fSPaolo Bonzini }
52449ab747fSPaolo Bonzini
vmstate_pl110_post_load(void * opaque,int version_id)52549ab747fSPaolo Bonzini static int vmstate_pl110_post_load(void *opaque, int version_id)
52649ab747fSPaolo Bonzini {
527513960eaSAndreas Färber PL110State *s = opaque;
52849ab747fSPaolo Bonzini /* Make sure we redraw, and at the right size */
52949ab747fSPaolo Bonzini pl110_invalidate_display(s);
53049ab747fSPaolo Bonzini return 0;
53149ab747fSPaolo Bonzini }
53249ab747fSPaolo Bonzini
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é
pl110_realize(DeviceState * dev,Error ** errp)544caae8032Sxiaoqiang zhao static void pl110_realize(DeviceState *dev, Error **errp)
54549ab747fSPaolo Bonzini {
5465d7a11e4SAndreas Färber PL110State *s = PL110(dev);
547caae8032Sxiaoqiang zhao SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
54849ab747fSPaolo Bonzini
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);
56149ab747fSPaolo Bonzini }
56249ab747fSPaolo Bonzini
pl110_init(Object * obj)5635d7a11e4SAndreas Färber static void pl110_init(Object *obj)
56449ab747fSPaolo Bonzini {
5655d7a11e4SAndreas Färber PL110State *s = PL110(obj);
5665d7a11e4SAndreas Färber
567c7bf3492SEduardo Habkost s->version = VERSION_PL110;
56849ab747fSPaolo Bonzini }
56949ab747fSPaolo Bonzini
pl110_versatile_init(Object * obj)5705d7a11e4SAndreas Färber static void pl110_versatile_init(Object *obj)
57149ab747fSPaolo Bonzini {
5725d7a11e4SAndreas Färber PL110State *s = PL110(obj);
5735d7a11e4SAndreas Färber
574c7bf3492SEduardo Habkost s->version = VERSION_PL110_VERSATILE;
5755d7a11e4SAndreas Färber }
5765d7a11e4SAndreas Färber
pl111_init(Object * obj)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;
58249ab747fSPaolo Bonzini }
58349ab747fSPaolo Bonzini
pl110_class_init(ObjectClass * klass,void * data)58449ab747fSPaolo Bonzini static void pl110_class_init(ObjectClass *klass, void *data)
58549ab747fSPaolo Bonzini {
58649ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS(klass);
58749ab747fSPaolo Bonzini
588125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
58949ab747fSPaolo Bonzini dc->vmsd = &vmstate_pl110;
590caae8032Sxiaoqiang zhao dc->realize = pl110_realize;
591*c2093660SPhilippe Mathieu-Daudé device_class_set_props(dc, pl110_properties);
59249ab747fSPaolo Bonzini }
59349ab747fSPaolo Bonzini
59449ab747fSPaolo Bonzini static const TypeInfo pl110_info = {
5955d7a11e4SAndreas Färber .name = TYPE_PL110,
59649ab747fSPaolo Bonzini .parent = TYPE_SYS_BUS_DEVICE,
597513960eaSAndreas Färber .instance_size = sizeof(PL110State),
5985d7a11e4SAndreas Färber .instance_init = pl110_init,
59949ab747fSPaolo Bonzini .class_init = pl110_class_init,
60049ab747fSPaolo Bonzini };
60149ab747fSPaolo Bonzini
60249ab747fSPaolo Bonzini static const TypeInfo pl110_versatile_info = {
60349ab747fSPaolo Bonzini .name = "pl110_versatile",
6045d7a11e4SAndreas Färber .parent = TYPE_PL110,
6055d7a11e4SAndreas Färber .instance_init = pl110_versatile_init,
60649ab747fSPaolo Bonzini };
60749ab747fSPaolo Bonzini
60849ab747fSPaolo Bonzini static const TypeInfo pl111_info = {
60949ab747fSPaolo Bonzini .name = "pl111",
6105d7a11e4SAndreas Färber .parent = TYPE_PL110,
6115d7a11e4SAndreas Färber .instance_init = pl111_init,
61249ab747fSPaolo Bonzini };
61349ab747fSPaolo Bonzini
pl110_register_types(void)61449ab747fSPaolo Bonzini static void pl110_register_types(void)
61549ab747fSPaolo Bonzini {
61649ab747fSPaolo Bonzini type_register_static(&pl110_info);
61749ab747fSPaolo Bonzini type_register_static(&pl110_versatile_info);
61849ab747fSPaolo Bonzini type_register_static(&pl111_info);
61949ab747fSPaolo Bonzini }
62049ab747fSPaolo Bonzini
62149ab747fSPaolo Bonzini type_init(pl110_register_types)
622