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
;
172 if (pl110_enabled(s
)) {
173 qemu_console_resize(s
->ds
, s
->cols
, s
->rows
);
177 static void pl110_update_pallette(pl110_state
*s
, int n
)
181 unsigned int r
, g
, b
;
183 raw
= s
->raw_pallette
[n
];
185 for (i
= 0; i
< 2; i
++) {
186 r
= (raw
& 0x1f) << 3;
188 g
= (raw
& 0x1f) << 3;
190 b
= (raw
& 0x1f) << 3;
191 /* The I bit is ignored. */
193 switch (ds_get_bits_per_pixel(s
->ds
)) {
195 s
->pallette
[n
] = rgb_to_pixel8(r
, g
, b
);
198 s
->pallette
[n
] = rgb_to_pixel15(r
, g
, b
);
201 s
->pallette
[n
] = rgb_to_pixel16(r
, g
, b
);
205 s
->pallette
[n
] = rgb_to_pixel32(r
, g
, b
);
212 static void pl110_resize(pl110_state
*s
, int width
, int height
)
214 if (width
!= s
->cols
|| height
!= s
->rows
) {
215 if (pl110_enabled(s
)) {
216 qemu_console_resize(s
->ds
, width
, height
);
223 /* Update interrupts. */
224 static void pl110_update(pl110_state
*s
)
226 /* TODO: Implement interrupts. */
229 static uint32_t pl110_read(void *opaque
, target_phys_addr_t offset
)
231 pl110_state
*s
= (pl110_state
*)opaque
;
233 if (offset
>= 0xfe0 && offset
< 0x1000) {
235 return pl110_versatile_id
[(offset
- 0xfe0) >> 2];
237 return pl110_id
[(offset
- 0xfe0) >> 2];
239 if (offset
>= 0x200 && offset
< 0x400) {
240 return s
->raw_pallette
[(offset
- 0x200) >> 2];
242 switch (offset
>> 2) {
243 case 0: /* LCDTiming0 */
245 case 1: /* LCDTiming1 */
247 case 2: /* LCDTiming2 */
249 case 3: /* LCDTiming3 */
251 case 4: /* LCDUPBASE */
253 case 5: /* LCDLPBASE */
255 case 6: /* LCDIMSC */
259 case 7: /* LCDControl */
264 return s
->int_status
;
266 return s
->int_status
& s
->int_mask
;
267 case 11: /* LCDUPCURR */
268 /* TODO: Implement vertical refresh. */
270 case 12: /* LCDLPCURR */
273 hw_error("pl110_read: Bad offset %x\n", (int)offset
);
278 static void pl110_write(void *opaque
, target_phys_addr_t offset
,
281 pl110_state
*s
= (pl110_state
*)opaque
;
284 /* For simplicity invalidate the display whenever a control register
287 if (offset
>= 0x200 && offset
< 0x400) {
289 n
= (offset
- 0x200) >> 2;
290 s
->raw_pallette
[(offset
- 0x200) >> 2] = val
;
291 pl110_update_pallette(s
, n
);
294 switch (offset
>> 2) {
295 case 0: /* LCDTiming0 */
297 n
= ((val
& 0xfc) + 4) * 4;
298 pl110_resize(s
, n
, s
->rows
);
300 case 1: /* LCDTiming1 */
302 n
= (val
& 0x3ff) + 1;
303 pl110_resize(s
, s
->cols
, n
);
305 case 2: /* LCDTiming2 */
308 case 3: /* LCDTiming3 */
311 case 4: /* LCDUPBASE */
314 case 5: /* LCDLPBASE */
317 case 6: /* LCDIMSC */
324 case 7: /* LCDControl */
329 s
->bpp
= (val
>> 1) & 7;
330 if (pl110_enabled(s
)) {
331 qemu_console_resize(s
->ds
, s
->cols
, s
->rows
);
334 case 10: /* LCDICR */
335 s
->int_status
&= ~val
;
339 hw_error("pl110_write: Bad offset %x\n", (int)offset
);
343 static CPUReadMemoryFunc
*pl110_readfn
[] = {
349 static CPUWriteMemoryFunc
*pl110_writefn
[] = {
355 static void pl110_init(SysBusDevice
*dev
)
357 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
360 iomemtype
= cpu_register_io_memory(pl110_readfn
,
362 sysbus_init_mmio(dev
, 0x1000, iomemtype
);
363 sysbus_init_irq(dev
, &s
->irq
);
364 s
->ds
= graphic_console_init(pl110_update_display
,
365 pl110_invalidate_display
,
367 /* ??? Save/restore. */
370 static void pl110_versatile_init(SysBusDevice
*dev
)
372 pl110_state
*s
= FROM_SYSBUS(pl110_state
, dev
);
377 static void pl110_register_devices(void)
379 sysbus_register_dev("pl110", sizeof(pl110_state
), pl110_init
);
380 sysbus_register_dev("pl110_versatile", sizeof(pl110_state
),
381 pl110_versatile_init
);
384 device_init(pl110_register_devices
)