2 * Arm PrimeCell PL110 Color LCD Controller
4 * Copyright (c) 2005-2009 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licensed under the GNU LGPL
10 #include "qemu/osdep.h"
12 #include "hw/sysbus.h"
13 #include "migration/vmstate.h"
14 #include "ui/console.h"
15 #include "framebuffer.h"
16 #include "ui/pixel_ops.h"
17 #include "qemu/timer.h"
19 #include "qemu/module.h"
20 #include "qom/object.h"
22 #define PL110_CR_EN 0x001
23 #define PL110_CR_BGR 0x100
24 #define PL110_CR_BEBO 0x200
25 #define PL110_CR_BEPO 0x400
26 #define PL110_CR_PWR 0x800
27 #define PL110_IE_NB 0x004
28 #define PL110_IE_VC 0x008
38 BPP_16_565
, /* PL111 only */
39 BPP_12
/* PL111 only */
43 /* The Versatile/PB uses a slightly modified PL110 controller. */
47 VERSION_PL110_VERSATILE
,
51 #define TYPE_PL110 "pl110"
52 typedef struct PL110State PL110State
;
53 DECLARE_INSTANCE_CHECKER(PL110State
, PL110
,
57 SysBusDevice parent_obj
;
60 MemoryRegionSection fbsection
;
62 QEMUTimer
*vblank_timer
;
73 enum pl110_bppmode bpp
;
76 uint32_t palette
[256];
77 uint32_t raw_palette
[128];
81 static int vmstate_pl110_post_load(void *opaque
, int version_id
);
83 static const VMStateDescription vmstate_pl110
= {
86 .minimum_version_id
= 1,
87 .post_load
= vmstate_pl110_post_load
,
88 .fields
= (VMStateField
[]) {
89 VMSTATE_INT32(version
, PL110State
),
90 VMSTATE_UINT32_ARRAY(timing
, PL110State
, 4),
91 VMSTATE_UINT32(cr
, PL110State
),
92 VMSTATE_UINT32(upbase
, PL110State
),
93 VMSTATE_UINT32(lpbase
, PL110State
),
94 VMSTATE_UINT32(int_status
, PL110State
),
95 VMSTATE_UINT32(int_mask
, PL110State
),
96 VMSTATE_INT32(cols
, PL110State
),
97 VMSTATE_INT32(rows
, PL110State
),
98 VMSTATE_UINT32(bpp
, PL110State
),
99 VMSTATE_INT32(invalidate
, PL110State
),
100 VMSTATE_UINT32_ARRAY(palette
, PL110State
, 256),
101 VMSTATE_UINT32_ARRAY(raw_palette
, PL110State
, 128),
102 VMSTATE_UINT32_V(mux_ctrl
, PL110State
, 2),
103 VMSTATE_END_OF_LIST()
107 static const unsigned char pl110_id
[] =
108 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
110 static const unsigned char pl111_id
[] = {
111 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
115 /* Indexed by pl110_version */
116 static const unsigned char *idregs
[] = {
118 /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board
119 * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware
120 * itself has the same ID values as a stock PL110, and guests (in
121 * particular Linux) rely on this. We emulate what the hardware does,
122 * rather than what the docs claim it ought to do.
129 #include "pl110_template.h"
131 #include "pl110_template.h"
133 #include "pl110_template.h"
135 #include "pl110_template.h"
137 #include "pl110_template.h"
139 static int pl110_enabled(PL110State
*s
)
141 return (s
->cr
& PL110_CR_EN
) && (s
->cr
& PL110_CR_PWR
);
144 static void pl110_update_display(void *opaque
)
146 PL110State
*s
= (PL110State
*)opaque
;
148 DisplaySurface
*surface
= qemu_console_surface(s
->con
);
157 if (!pl110_enabled(s
)) {
161 sbd
= SYS_BUS_DEVICE(s
);
163 switch (surface_bits_per_pixel(surface
)) {
167 fntable
= pl110_draw_fn_8
;
171 fntable
= pl110_draw_fn_15
;
175 fntable
= pl110_draw_fn_16
;
179 fntable
= pl110_draw_fn_24
;
183 fntable
= pl110_draw_fn_32
;
187 fprintf(stderr
, "pl110: Bad color depth\n");
190 if (s
->cr
& PL110_CR_BGR
)
195 if ((s
->version
!= VERSION_PL111
) && (s
->bpp
== BPP_16
)) {
196 /* The PL110's native 16 bit mode is 5551; however
197 * most boards with a PL110 implement an external
198 * mux which allows bits to be reshuffled to give
199 * 565 format. The mux is typically controlled by
200 * an external system register.
201 * This is controlled by a GPIO input pin
202 * so boards can wire it up to their register.
204 * The PL111 straightforwardly implements both
205 * 5551 and 565 under control of the bpp field
206 * in the LCDControl register.
208 switch (s
->mux_ctrl
) {
209 case 3: /* 565 BGR */
210 bpp_offset
= (BPP_16_565
- BPP_16
);
214 case 0: /* 888; also if we have loaded vmstate from an old version */
215 case 2: /* 565 RGB */
217 /* treat as 565 but honour BGR bit */
218 bpp_offset
+= (BPP_16_565
- BPP_16
);
223 if (s
->cr
& PL110_CR_BEBO
)
224 fn
= fntable
[s
->bpp
+ 8 + bpp_offset
];
225 else if (s
->cr
& PL110_CR_BEPO
)
226 fn
= fntable
[s
->bpp
+ 16 + bpp_offset
];
228 fn
= fntable
[s
->bpp
+ bpp_offset
];
252 dest_width
*= s
->cols
;
255 framebuffer_update_memory_section(&s
->fbsection
,
256 sysbus_address_space(sbd
),
261 framebuffer_update_display(surface
, &s
->fbsection
,
263 src_width
, dest_width
, 0,
269 dpy_gfx_update(s
->con
, 0, first
, s
->cols
, last
- first
+ 1);
274 static void pl110_invalidate_display(void * opaque
)
276 PL110State
*s
= (PL110State
*)opaque
;
278 if (pl110_enabled(s
)) {
279 qemu_console_resize(s
->con
, s
->cols
, s
->rows
);
283 static void pl110_update_palette(PL110State
*s
, int n
)
285 DisplaySurface
*surface
= qemu_console_surface(s
->con
);
288 unsigned int r
, g
, b
;
290 raw
= s
->raw_palette
[n
];
292 for (i
= 0; i
< 2; i
++) {
293 r
= (raw
& 0x1f) << 3;
295 g
= (raw
& 0x1f) << 3;
297 b
= (raw
& 0x1f) << 3;
298 /* The I bit is ignored. */
300 switch (surface_bits_per_pixel(surface
)) {
302 s
->palette
[n
] = rgb_to_pixel8(r
, g
, b
);
305 s
->palette
[n
] = rgb_to_pixel15(r
, g
, b
);
308 s
->palette
[n
] = rgb_to_pixel16(r
, g
, b
);
312 s
->palette
[n
] = rgb_to_pixel32(r
, g
, b
);
319 static void pl110_resize(PL110State
*s
, int width
, int height
)
321 if (width
!= s
->cols
|| height
!= s
->rows
) {
322 if (pl110_enabled(s
)) {
323 qemu_console_resize(s
->con
, width
, height
);
330 /* Update interrupts. */
331 static void pl110_update(PL110State
*s
)
333 /* Raise IRQ if enabled and any status bit is 1 */
334 if (s
->int_status
& s
->int_mask
) {
335 qemu_irq_raise(s
->irq
);
337 qemu_irq_lower(s
->irq
);
341 static void pl110_vblank_interrupt(void *opaque
)
343 PL110State
*s
= opaque
;
345 /* Fire the vertical compare and next base IRQs and re-arm */
346 s
->int_status
|= (PL110_IE_NB
| PL110_IE_VC
);
347 timer_mod(s
->vblank_timer
,
348 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
349 NANOSECONDS_PER_SECOND
/ 60);
353 static uint64_t pl110_read(void *opaque
, hwaddr offset
,
356 PL110State
*s
= (PL110State
*)opaque
;
358 if (offset
>= 0xfe0 && offset
< 0x1000) {
359 return idregs
[s
->version
][(offset
- 0xfe0) >> 2];
361 if (offset
>= 0x200 && offset
< 0x400) {
362 return s
->raw_palette
[(offset
- 0x200) >> 2];
364 switch (offset
>> 2) {
365 case 0: /* LCDTiming0 */
367 case 1: /* LCDTiming1 */
369 case 2: /* LCDTiming2 */
371 case 3: /* LCDTiming3 */
373 case 4: /* LCDUPBASE */
375 case 5: /* LCDLPBASE */
377 case 6: /* LCDIMSC */
378 if (s
->version
!= VERSION_PL110
) {
382 case 7: /* LCDControl */
383 if (s
->version
!= VERSION_PL110
) {
388 return s
->int_status
;
390 return s
->int_status
& s
->int_mask
;
391 case 11: /* LCDUPCURR */
392 /* TODO: Implement vertical refresh. */
394 case 12: /* LCDLPCURR */
397 qemu_log_mask(LOG_GUEST_ERROR
,
398 "pl110_read: Bad offset %x\n", (int)offset
);
403 static void pl110_write(void *opaque
, hwaddr offset
,
404 uint64_t val
, unsigned size
)
406 PL110State
*s
= (PL110State
*)opaque
;
409 /* For simplicity invalidate the display whenever a control register
412 if (offset
>= 0x200 && offset
< 0x400) {
414 n
= (offset
- 0x200) >> 2;
415 s
->raw_palette
[(offset
- 0x200) >> 2] = val
;
416 pl110_update_palette(s
, n
);
419 switch (offset
>> 2) {
420 case 0: /* LCDTiming0 */
422 n
= ((val
& 0xfc) + 4) * 4;
423 pl110_resize(s
, n
, s
->rows
);
425 case 1: /* LCDTiming1 */
427 n
= (val
& 0x3ff) + 1;
428 pl110_resize(s
, s
->cols
, n
);
430 case 2: /* LCDTiming2 */
433 case 3: /* LCDTiming3 */
436 case 4: /* LCDUPBASE */
439 case 5: /* LCDLPBASE */
442 case 6: /* LCDIMSC */
443 if (s
->version
!= VERSION_PL110
) {
450 case 7: /* LCDControl */
451 if (s
->version
!= VERSION_PL110
) {
456 s
->bpp
= (val
>> 1) & 7;
457 if (pl110_enabled(s
)) {
458 qemu_console_resize(s
->con
, s
->cols
, s
->rows
);
459 timer_mod(s
->vblank_timer
,
460 qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
461 NANOSECONDS_PER_SECOND
/ 60);
463 timer_del(s
->vblank_timer
);
466 case 10: /* LCDICR */
467 s
->int_status
&= ~val
;
471 qemu_log_mask(LOG_GUEST_ERROR
,
472 "pl110_write: Bad offset %x\n", (int)offset
);
476 static const MemoryRegionOps pl110_ops
= {
478 .write
= pl110_write
,
479 .endianness
= DEVICE_NATIVE_ENDIAN
,
482 static void pl110_mux_ctrl_set(void *opaque
, int line
, int level
)
484 PL110State
*s
= (PL110State
*)opaque
;
488 static int vmstate_pl110_post_load(void *opaque
, int version_id
)
490 PL110State
*s
= opaque
;
491 /* Make sure we redraw, and at the right size */
492 pl110_invalidate_display(s
);
496 static const GraphicHwOps pl110_gfx_ops
= {
497 .invalidate
= pl110_invalidate_display
,
498 .gfx_update
= pl110_update_display
,
501 static void pl110_realize(DeviceState
*dev
, Error
**errp
)
503 PL110State
*s
= PL110(dev
);
504 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
506 memory_region_init_io(&s
->iomem
, OBJECT(s
), &pl110_ops
, s
, "pl110", 0x1000);
507 sysbus_init_mmio(sbd
, &s
->iomem
);
508 sysbus_init_irq(sbd
, &s
->irq
);
509 s
->vblank_timer
= timer_new_ns(QEMU_CLOCK_VIRTUAL
,
510 pl110_vblank_interrupt
, s
);
511 qdev_init_gpio_in(dev
, pl110_mux_ctrl_set
, 1);
512 s
->con
= graphic_console_init(dev
, 0, &pl110_gfx_ops
, s
);
515 static void pl110_init(Object
*obj
)
517 PL110State
*s
= PL110(obj
);
519 s
->version
= VERSION_PL110
;
522 static void pl110_versatile_init(Object
*obj
)
524 PL110State
*s
= PL110(obj
);
526 s
->version
= VERSION_PL110_VERSATILE
;
529 static void pl111_init(Object
*obj
)
531 PL110State
*s
= PL110(obj
);
533 s
->version
= VERSION_PL111
;
536 static void pl110_class_init(ObjectClass
*klass
, void *data
)
538 DeviceClass
*dc
= DEVICE_CLASS(klass
);
540 set_bit(DEVICE_CATEGORY_DISPLAY
, dc
->categories
);
541 dc
->vmsd
= &vmstate_pl110
;
542 dc
->realize
= pl110_realize
;
545 static const TypeInfo pl110_info
= {
547 .parent
= TYPE_SYS_BUS_DEVICE
,
548 .instance_size
= sizeof(PL110State
),
549 .instance_init
= pl110_init
,
550 .class_init
= pl110_class_init
,
553 static const TypeInfo pl110_versatile_info
= {
554 .name
= "pl110_versatile",
555 .parent
= TYPE_PL110
,
556 .instance_init
= pl110_versatile_init
,
559 static const TypeInfo pl111_info
= {
561 .parent
= TYPE_PL110
,
562 .instance_init
= pl111_init
,
565 static void pl110_register_types(void)
567 type_register_static(&pl110_info
);
568 type_register_static(&pl110_versatile_info
);
569 type_register_static(&pl111_info
);
572 type_init(pl110_register_types
)