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
12 #include "framebuffer.h"
14 #define PL110_CR_EN 0x001
15 #define PL110_CR_BGR 0x100
16 #define PL110_CR_BEBO 0x200
17 #define PL110_CR_BEPO 0x400
18 #define PL110_CR_PWR 0x800
28 BPP_16_565
, /* PL111 only */
29 BPP_12
/* PL111 only */
33 /* The Versatile/PB uses a slightly modified PL110 controller. */
54 enum pl110_bppmode bpp
;
56 uint32_t pallette
[256];
57 uint32_t raw_pallette
[128];
61 static const VMStateDescription vmstate_pl110
= {
64 .minimum_version_id
= 1,
65 .fields
= (VMStateField
[]) {
66 VMSTATE_INT32(version
, pl110_state
),
67 VMSTATE_UINT32_ARRAY(timing
, pl110_state
, 4),
68 VMSTATE_UINT32(cr
, pl110_state
),
69 VMSTATE_UINT32(upbase
, pl110_state
),
70 VMSTATE_UINT32(lpbase
, pl110_state
),
71 VMSTATE_UINT32(int_status
, pl110_state
),
72 VMSTATE_UINT32(int_mask
, pl110_state
),
73 VMSTATE_INT32(cols
, pl110_state
),
74 VMSTATE_INT32(rows
, pl110_state
),
75 VMSTATE_UINT32(bpp
, pl110_state
),
76 VMSTATE_INT32(invalidate
, pl110_state
),
77 VMSTATE_UINT32_ARRAY(pallette
, pl110_state
, 256),
78 VMSTATE_UINT32_ARRAY(raw_pallette
, pl110_state
, 128),
83 static const unsigned char pl110_id
[] =
84 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
86 /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
87 has a different ID. However Linux only looks for the normal ID. */
89 static const unsigned char pl110_versatile_id
[] =
90 { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
92 #define pl110_versatile_id pl110_id
95 static const unsigned char pl111_id
[] = {
96 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
99 /* Indexed by pl110_version */
100 static const unsigned char *idregs
[] = {
106 #include "pixel_ops.h"
109 #include "pl110_template.h"
111 #include "pl110_template.h"
113 #include "pl110_template.h"
115 #include "pl110_template.h"
117 #include "pl110_template.h"
119 static int pl110_enabled(pl110_state
*s
)
121 return (s
->cr
& PL110_CR_EN
) && (s
->cr
& PL110_CR_PWR
);
124 static void pl110_update_display(void *opaque
)
126 pl110_state
*s
= (pl110_state
*)opaque
;
135 if (!pl110_enabled(s
))
138 switch (ds_get_bits_per_pixel(s
->ds
)) {
142 fntable
= pl110_draw_fn_8
;
146 fntable
= pl110_draw_fn_15
;
150 fntable
= pl110_draw_fn_16
;
154 fntable
= pl110_draw_fn_24
;
158 fntable
= pl110_draw_fn_32
;
162 fprintf(stderr
, "pl110: Bad color depth\n");
165 if (s
->cr
& PL110_CR_BGR
)
170 if ((s
->version
!= PL111
) && (s
->bpp
== BPP_16
)) {
171 /* The PL110's native 16 bit mode is 5551; however
172 * most boards with a PL110 implement an external
173 * mux which allows bits to be reshuffled to give
174 * 565 format. The mux is typically controlled by
175 * an external system register.
176 * This should be controlled by a GPIO input pin
177 * so boards can wire it up to their register.
178 * For now, force 16 bit to be 565, to match
179 * previous QEMU PL110 model behaviour.
181 * The PL111 straightforwardly implements both
182 * 5551 and 565 under control of the bpp field
183 * in the LCDControl register.
185 bpp_offset
+= (BPP_16_565
- BPP_16
);
188 if (s
->cr
& PL110_CR_BEBO
)
189 fn
= fntable
[s
->bpp
+ 8 + bpp_offset
];
190 else if (s
->cr
& PL110_CR_BEPO
)
191 fn
= fntable
[s
->bpp
+ 16 + bpp_offset
];
193 fn
= fntable
[s
->bpp
+ bpp_offset
];
217 dest_width
*= s
->cols
;
219 framebuffer_update_display(s
->ds
,
220 s
->upbase
, s
->cols
, s
->rows
,
221 src_width
, dest_width
, 0,
226 dpy_update(s
->ds
, 0, first
, s
->cols
, last
- first
+ 1);
231 static void pl110_invalidate_display(void * opaque
)
233 pl110_state
*s
= (pl110_state
*)opaque
;
235 if (pl110_enabled(s
)) {
236 qemu_console_resize(s
->ds
, s
->cols
, s
->rows
);
240 static void pl110_update_pallette(pl110_state
*s
, int n
)
244 unsigned int r
, g
, b
;
246 raw
= s
->raw_pallette
[n
];
248 for (i
= 0; i
< 2; i
++) {
249 r
= (raw
& 0x1f) << 3;
251 g
= (raw
& 0x1f) << 3;
253 b
= (raw
& 0x1f) << 3;
254 /* The I bit is ignored. */
256 switch (ds_get_bits_per_pixel(s
->ds
)) {
258 s
->pallette
[n
] = rgb_to_pixel8(r
, g
, b
);
261 s
->pallette
[n
] = rgb_to_pixel15(r
, g
, b
);
264 s
->pallette
[n
] = rgb_to_pixel16(r
, g
, b
);
268 s
->pallette
[n
] = rgb_to_pixel32(r
, g
, b
);
275 static void pl110_resize(pl110_state
*s
, int width
, int height
)
277 if (width
!= s
->cols
|| height
!= s
->rows
) {
278 if (pl110_enabled(s
)) {
279 qemu_console_resize(s
->ds
, width
, height
);
286 /* Update interrupts. */
287 static void pl110_update(pl110_state
*s
)
289 /* TODO: Implement interrupts. */
292 static uint32_t pl110_read(void *opaque
, target_phys_addr_t offset
)
294 pl110_state
*s
= (pl110_state
*)opaque
;
296 if (offset
>= 0xfe0 && offset
< 0x1000) {
297 return idregs
[s
->version
][(offset
- 0xfe0) >> 2];
299 if (offset
>= 0x200 && offset
< 0x400) {
300 return s
->raw_pallette
[(offset
- 0x200) >> 2];
302 switch (offset
>> 2) {
303 case 0: /* LCDTiming0 */
305 case 1: /* LCDTiming1 */
307 case 2: /* LCDTiming2 */
309 case 3: /* LCDTiming3 */
311 case 4: /* LCDUPBASE */
313 case 5: /* LCDLPBASE */
315 case 6: /* LCDIMSC */
316 if (s
->version
!= PL110
) {
320 case 7: /* LCDControl */
321 if (s
->version
!= PL110
) {
326 return s
->int_status
;
328 return s
->int_status
& s
->int_mask
;
329 case 11: /* LCDUPCURR */
330 /* TODO: Implement vertical refresh. */
332 case 12: /* LCDLPCURR */
335 hw_error("pl110_read: Bad offset %x\n", (int)offset
);
340 static void pl110_write(void *opaque
, target_phys_addr_t offset
,
343 pl110_state
*s
= (pl110_state
*)opaque
;
346 /* For simplicity invalidate the display whenever a control register
349 if (offset
>= 0x200 && offset
< 0x400) {
351 n
= (offset
- 0x200) >> 2;
352 s
->raw_pallette
[(offset
- 0x200) >> 2] = val
;
353 pl110_update_pallette(s
, n
);
356 switch (offset
>> 2) {
357 case 0: /* LCDTiming0 */
359 n
= ((val
& 0xfc) + 4) * 4;
360 pl110_resize(s
, n
, s
->rows
);
362 case 1: /* LCDTiming1 */
364 n
= (val
& 0x3ff) + 1;
365 pl110_resize(s
, s
->cols
, n
);
367 case 2: /* LCDTiming2 */
370 case 3: /* LCDTiming3 */
373 case 4: /* LCDUPBASE */
376 case 5: /* LCDLPBASE */
379 case 6: /* LCDIMSC */
380 if (s
->version
!= PL110
) {
387 case 7: /* LCDControl */
388 if (s
->version
!= PL110
) {
393 s
->bpp
= (val
>> 1) & 7;
394 if (pl110_enabled(s
)) {
395 qemu_console_resize(s
->ds
, s
->cols
, s
->rows
);
398 case 10: /* LCDICR */
399 s
->int_status
&= ~val
;
403 hw_error("pl110_write: Bad offset %x\n", (int)offset
);
407 static CPUReadMemoryFunc
* const pl110_readfn
[] = {
413 static CPUWriteMemoryFunc
* const pl110_writefn
[] = {
419 static int pl110_init(SysBusDevice
*dev
)
421 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
424 iomemtype
= cpu_register_io_memory(pl110_readfn
,
426 DEVICE_NATIVE_ENDIAN
);
427 sysbus_init_mmio(dev
, 0x1000, iomemtype
);
428 sysbus_init_irq(dev
, &s
->irq
);
429 s
->ds
= graphic_console_init(pl110_update_display
,
430 pl110_invalidate_display
,
435 static int pl110_versatile_init(SysBusDevice
*dev
)
437 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
438 s
->version
= PL110_VERSATILE
;
439 return pl110_init(dev
);
442 static int pl111_init(SysBusDevice
*dev
)
444 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
446 return pl110_init(dev
);
449 static SysBusDeviceInfo pl110_info
= {
451 .qdev
.name
= "pl110",
452 .qdev
.size
= sizeof(pl110_state
),
453 .qdev
.vmsd
= &vmstate_pl110
,
457 static SysBusDeviceInfo pl110_versatile_info
= {
458 .init
= pl110_versatile_init
,
459 .qdev
.name
= "pl110_versatile",
460 .qdev
.size
= sizeof(pl110_state
),
461 .qdev
.vmsd
= &vmstate_pl110
,
465 static SysBusDeviceInfo pl111_info
= {
467 .qdev
.name
= "pl111",
468 .qdev
.size
= sizeof(pl110_state
),
469 .qdev
.vmsd
= &vmstate_pl110
,
473 static void pl110_register_devices(void)
475 sysbus_register_withprop(&pl110_info
);
476 sysbus_register_withprop(&pl110_versatile_info
);
477 sysbus_register_withprop(&pl111_info
);
480 device_init(pl110_register_devices
)