2 * Arm PrimeCell PL110 Color LCD Controller
4 * Copyright (c) 2005-2009 CodeSourcery.
5 * Written by Paul Brook
7 * This code is licenced 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
34 /* The Versatile/PB uses a slightly modified PL110 controller. */
44 enum pl110_bppmode bpp
;
46 uint32_t pallette
[256];
47 uint32_t raw_pallette
[128];
51 static const unsigned char pl110_id
[] =
52 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
54 /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
55 has a different ID. However Linux only looks for the normal ID. */
57 static const unsigned char pl110_versatile_id
[] =
58 { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
60 #define pl110_versatile_id pl110_id
63 #include "pixel_ops.h"
66 #include "pl110_template.h"
68 #include "pl110_template.h"
70 #include "pl110_template.h"
72 #include "pl110_template.h"
74 #include "pl110_template.h"
76 static int pl110_enabled(pl110_state
*s
)
78 return (s
->cr
& PL110_CR_EN
) && (s
->cr
& PL110_CR_PWR
);
81 static void pl110_update_display(void *opaque
)
83 pl110_state
*s
= (pl110_state
*)opaque
;
92 if (!pl110_enabled(s
))
95 switch (ds_get_bits_per_pixel(s
->ds
)) {
99 fntable
= pl110_draw_fn_8
;
103 fntable
= pl110_draw_fn_15
;
107 fntable
= pl110_draw_fn_16
;
111 fntable
= pl110_draw_fn_24
;
115 fntable
= pl110_draw_fn_32
;
119 fprintf(stderr
, "pl110: Bad color depth\n");
122 if (s
->cr
& PL110_CR_BGR
)
127 if (s
->cr
& PL110_CR_BEBO
)
128 fn
= fntable
[s
->bpp
+ 6 + bpp_offset
];
129 else if (s
->cr
& PL110_CR_BEPO
)
130 fn
= fntable
[s
->bpp
+ 12 + bpp_offset
];
132 fn
= fntable
[s
->bpp
+ bpp_offset
];
154 dest_width
*= s
->cols
;
156 framebuffer_update_display(s
->ds
,
157 s
->upbase
, s
->cols
, s
->rows
,
158 src_width
, dest_width
, 0,
163 dpy_update(s
->ds
, 0, first
, s
->cols
, last
- first
+ 1);
168 static void pl110_invalidate_display(void * opaque
)
170 pl110_state
*s
= (pl110_state
*)opaque
;
174 static void pl110_update_pallette(pl110_state
*s
, int n
)
178 unsigned int r
, g
, b
;
180 raw
= s
->raw_pallette
[n
];
182 for (i
= 0; i
< 2; i
++) {
183 r
= (raw
& 0x1f) << 3;
185 g
= (raw
& 0x1f) << 3;
187 b
= (raw
& 0x1f) << 3;
188 /* The I bit is ignored. */
190 switch (ds_get_bits_per_pixel(s
->ds
)) {
192 s
->pallette
[n
] = rgb_to_pixel8(r
, g
, b
);
195 s
->pallette
[n
] = rgb_to_pixel15(r
, g
, b
);
198 s
->pallette
[n
] = rgb_to_pixel16(r
, g
, b
);
202 s
->pallette
[n
] = rgb_to_pixel32(r
, g
, b
);
209 static void pl110_resize(pl110_state
*s
, int width
, int height
)
211 if (width
!= s
->cols
|| height
!= s
->rows
) {
212 if (pl110_enabled(s
)) {
213 qemu_console_resize(s
->ds
, width
, height
);
220 /* Update interrupts. */
221 static void pl110_update(pl110_state
*s
)
223 /* TODO: Implement interrupts. */
226 static uint32_t pl110_read(void *opaque
, target_phys_addr_t offset
)
228 pl110_state
*s
= (pl110_state
*)opaque
;
230 if (offset
>= 0xfe0 && offset
< 0x1000) {
232 return pl110_versatile_id
[(offset
- 0xfe0) >> 2];
234 return pl110_id
[(offset
- 0xfe0) >> 2];
236 if (offset
>= 0x200 && offset
< 0x400) {
237 return s
->raw_pallette
[(offset
- 0x200) >> 2];
239 switch (offset
>> 2) {
240 case 0: /* LCDTiming0 */
242 case 1: /* LCDTiming1 */
244 case 2: /* LCDTiming2 */
246 case 3: /* LCDTiming3 */
248 case 4: /* LCDUPBASE */
250 case 5: /* LCDLPBASE */
252 case 6: /* LCDIMSC */
256 case 7: /* LCDControl */
261 return s
->int_status
;
263 return s
->int_status
& s
->int_mask
;
264 case 11: /* LCDUPCURR */
265 /* TODO: Implement vertical refresh. */
267 case 12: /* LCDLPCURR */
270 hw_error("pl110_read: Bad offset %x\n", (int)offset
);
275 static void pl110_write(void *opaque
, target_phys_addr_t offset
,
278 pl110_state
*s
= (pl110_state
*)opaque
;
281 /* For simplicity invalidate the display whenever a control register
284 if (offset
>= 0x200 && offset
< 0x400) {
286 n
= (offset
- 0x200) >> 2;
287 s
->raw_pallette
[(offset
- 0x200) >> 2] = val
;
288 pl110_update_pallette(s
, n
);
291 switch (offset
>> 2) {
292 case 0: /* LCDTiming0 */
294 n
= ((val
& 0xfc) + 4) * 4;
295 pl110_resize(s
, n
, s
->rows
);
297 case 1: /* LCDTiming1 */
299 n
= (val
& 0x3ff) + 1;
300 pl110_resize(s
, s
->cols
, n
);
302 case 2: /* LCDTiming2 */
305 case 3: /* LCDTiming3 */
308 case 4: /* LCDUPBASE */
311 case 5: /* LCDLPBASE */
314 case 6: /* LCDIMSC */
321 case 7: /* LCDControl */
326 s
->bpp
= (val
>> 1) & 7;
327 if (pl110_enabled(s
)) {
328 qemu_console_resize(s
->ds
, s
->cols
, s
->rows
);
331 case 10: /* LCDICR */
332 s
->int_status
&= ~val
;
336 hw_error("pl110_write: Bad offset %x\n", (int)offset
);
340 static CPUReadMemoryFunc
*pl110_readfn
[] = {
346 static CPUWriteMemoryFunc
*pl110_writefn
[] = {
352 static void pl110_init(SysBusDevice
*dev
)
354 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
357 iomemtype
= cpu_register_io_memory(0, pl110_readfn
,
359 sysbus_init_mmio(dev
, 0x1000, iomemtype
);
360 sysbus_init_irq(dev
, &s
->irq
);
361 s
->ds
= graphic_console_init(pl110_update_display
,
362 pl110_invalidate_display
,
364 /* ??? Save/restore. */
367 static void pl110_versatile_init(SysBusDevice
*dev
)
369 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
374 static void pl110_register_devices(void)
376 sysbus_register_dev("pl110", sizeof(pl110_state
), pl110_init
);
377 sysbus_register_dev("pl110_versatile", sizeof(pl110_state
),
378 pl110_versatile_init
);
381 device_init(pl110_register_devices
)