]> Git Repo - qemu.git/blob - hw/pl110.c
Merge branch 'target-arm.for-upstream' of git://git.linaro.org/people/pmaydell/qemu-arm
[qemu.git] / hw / pl110.c
1 /*
2  * Arm PrimeCell PL110 Color LCD Controller
3  *
4  * Copyright (c) 2005-2009 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licensed under the GNU LGPL
8  */
9
10 #include "sysbus.h"
11 #include "console.h"
12 #include "framebuffer.h"
13
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
19
20 enum pl110_bppmode
21 {
22     BPP_1,
23     BPP_2,
24     BPP_4,
25     BPP_8,
26     BPP_16,
27     BPP_32,
28     BPP_16_565, /* PL111 only */
29     BPP_12      /* PL111 only */
30 };
31
32
33 /* The Versatile/PB uses a slightly modified PL110 controller.  */
34 enum pl110_version
35 {
36     PL110,
37     PL110_VERSATILE,
38     PL111
39 };
40
41 typedef struct {
42     SysBusDevice busdev;
43     MemoryRegion iomem;
44     DisplayState *ds;
45
46     int version;
47     uint32_t timing[4];
48     uint32_t cr;
49     uint32_t upbase;
50     uint32_t lpbase;
51     uint32_t int_status;
52     uint32_t int_mask;
53     int cols;
54     int rows;
55     enum pl110_bppmode bpp;
56     int invalidate;
57     uint32_t mux_ctrl;
58     uint32_t pallette[256];
59     uint32_t raw_pallette[128];
60     qemu_irq irq;
61 } pl110_state;
62
63 static const VMStateDescription vmstate_pl110 = {
64     .name = "pl110",
65     .version_id = 2,
66     .minimum_version_id = 1,
67     .fields = (VMStateField[]) {
68         VMSTATE_INT32(version, pl110_state),
69         VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
70         VMSTATE_UINT32(cr, pl110_state),
71         VMSTATE_UINT32(upbase, pl110_state),
72         VMSTATE_UINT32(lpbase, pl110_state),
73         VMSTATE_UINT32(int_status, pl110_state),
74         VMSTATE_UINT32(int_mask, pl110_state),
75         VMSTATE_INT32(cols, pl110_state),
76         VMSTATE_INT32(rows, pl110_state),
77         VMSTATE_UINT32(bpp, pl110_state),
78         VMSTATE_INT32(invalidate, pl110_state),
79         VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256),
80         VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128),
81         VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2),
82         VMSTATE_END_OF_LIST()
83     }
84 };
85
86 static const unsigned char pl110_id[] =
87 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
88
89 /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
90    has a different ID.  However Linux only looks for the normal ID.  */
91 #if 0
92 static const unsigned char pl110_versatile_id[] =
93 { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
94 #else
95 #define pl110_versatile_id pl110_id
96 #endif
97
98 static const unsigned char pl111_id[] = {
99     0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
100 };
101
102 /* Indexed by pl110_version */
103 static const unsigned char *idregs[] = {
104     pl110_id,
105     pl110_versatile_id,
106     pl111_id
107 };
108
109 #include "pixel_ops.h"
110
111 #define BITS 8
112 #include "pl110_template.h"
113 #define BITS 15
114 #include "pl110_template.h"
115 #define BITS 16
116 #include "pl110_template.h"
117 #define BITS 24
118 #include "pl110_template.h"
119 #define BITS 32
120 #include "pl110_template.h"
121
122 static int pl110_enabled(pl110_state *s)
123 {
124   return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
125 }
126
127 static void pl110_update_display(void *opaque)
128 {
129     pl110_state *s = (pl110_state *)opaque;
130     drawfn* fntable;
131     drawfn fn;
132     int dest_width;
133     int src_width;
134     int bpp_offset;
135     int first;
136     int last;
137
138     if (!pl110_enabled(s))
139         return;
140
141     switch (ds_get_bits_per_pixel(s->ds)) {
142     case 0:
143         return;
144     case 8:
145         fntable = pl110_draw_fn_8;
146         dest_width = 1;
147         break;
148     case 15:
149         fntable = pl110_draw_fn_15;
150         dest_width = 2;
151         break;
152     case 16:
153         fntable = pl110_draw_fn_16;
154         dest_width = 2;
155         break;
156     case 24:
157         fntable = pl110_draw_fn_24;
158         dest_width = 3;
159         break;
160     case 32:
161         fntable = pl110_draw_fn_32;
162         dest_width = 4;
163         break;
164     default:
165         fprintf(stderr, "pl110: Bad color depth\n");
166         exit(1);
167     }
168     if (s->cr & PL110_CR_BGR)
169         bpp_offset = 0;
170     else
171         bpp_offset = 24;
172
173     if ((s->version != PL111) && (s->bpp == BPP_16)) {
174         /* The PL110's native 16 bit mode is 5551; however
175          * most boards with a PL110 implement an external
176          * mux which allows bits to be reshuffled to give
177          * 565 format. The mux is typically controlled by
178          * an external system register.
179          * This is controlled by a GPIO input pin
180          * so boards can wire it up to their register.
181          *
182          * The PL111 straightforwardly implements both
183          * 5551 and 565 under control of the bpp field
184          * in the LCDControl register.
185          */
186         switch (s->mux_ctrl) {
187         case 3: /* 565 BGR */
188             bpp_offset = (BPP_16_565 - BPP_16);
189             break;
190         case 1: /* 5551 */
191             break;
192         case 0: /* 888; also if we have loaded vmstate from an old version */
193         case 2: /* 565 RGB */
194         default:
195             /* treat as 565 but honour BGR bit */
196             bpp_offset += (BPP_16_565 - BPP_16);
197             break;
198         }
199     }
200
201     if (s->cr & PL110_CR_BEBO)
202         fn = fntable[s->bpp + 8 + bpp_offset];
203     else if (s->cr & PL110_CR_BEPO)
204         fn = fntable[s->bpp + 16 + bpp_offset];
205     else
206         fn = fntable[s->bpp + bpp_offset];
207
208     src_width = s->cols;
209     switch (s->bpp) {
210     case BPP_1:
211         src_width >>= 3;
212         break;
213     case BPP_2:
214         src_width >>= 2;
215         break;
216     case BPP_4:
217         src_width >>= 1;
218         break;
219     case BPP_8:
220         break;
221     case BPP_16:
222     case BPP_16_565:
223     case BPP_12:
224         src_width <<= 1;
225         break;
226     case BPP_32:
227         src_width <<= 2;
228         break;
229     }
230     dest_width *= s->cols;
231     first = 0;
232     framebuffer_update_display(s->ds,
233                                s->upbase, s->cols, s->rows,
234                                src_width, dest_width, 0,
235                                s->invalidate,
236                                fn, s->pallette,
237                                &first, &last);
238     if (first >= 0) {
239         dpy_update(s->ds, 0, first, s->cols, last - first + 1);
240     }
241     s->invalidate = 0;
242 }
243
244 static void pl110_invalidate_display(void * opaque)
245 {
246     pl110_state *s = (pl110_state *)opaque;
247     s->invalidate = 1;
248     if (pl110_enabled(s)) {
249         qemu_console_resize(s->ds, s->cols, s->rows);
250     }
251 }
252
253 static void pl110_update_pallette(pl110_state *s, int n)
254 {
255     int i;
256     uint32_t raw;
257     unsigned int r, g, b;
258
259     raw = s->raw_pallette[n];
260     n <<= 1;
261     for (i = 0; i < 2; i++) {
262         r = (raw & 0x1f) << 3;
263         raw >>= 5;
264         g = (raw & 0x1f) << 3;
265         raw >>= 5;
266         b = (raw & 0x1f) << 3;
267         /* The I bit is ignored.  */
268         raw >>= 6;
269         switch (ds_get_bits_per_pixel(s->ds)) {
270         case 8:
271             s->pallette[n] = rgb_to_pixel8(r, g, b);
272             break;
273         case 15:
274             s->pallette[n] = rgb_to_pixel15(r, g, b);
275             break;
276         case 16:
277             s->pallette[n] = rgb_to_pixel16(r, g, b);
278             break;
279         case 24:
280         case 32:
281             s->pallette[n] = rgb_to_pixel32(r, g, b);
282             break;
283         }
284         n++;
285     }
286 }
287
288 static void pl110_resize(pl110_state *s, int width, int height)
289 {
290     if (width != s->cols || height != s->rows) {
291         if (pl110_enabled(s)) {
292             qemu_console_resize(s->ds, width, height);
293         }
294     }
295     s->cols = width;
296     s->rows = height;
297 }
298
299 /* Update interrupts.  */
300 static void pl110_update(pl110_state *s)
301 {
302   /* TODO: Implement interrupts.  */
303 }
304
305 static uint64_t pl110_read(void *opaque, target_phys_addr_t offset,
306                            unsigned size)
307 {
308     pl110_state *s = (pl110_state *)opaque;
309
310     if (offset >= 0xfe0 && offset < 0x1000) {
311         return idregs[s->version][(offset - 0xfe0) >> 2];
312     }
313     if (offset >= 0x200 && offset < 0x400) {
314         return s->raw_pallette[(offset - 0x200) >> 2];
315     }
316     switch (offset >> 2) {
317     case 0: /* LCDTiming0 */
318         return s->timing[0];
319     case 1: /* LCDTiming1 */
320         return s->timing[1];
321     case 2: /* LCDTiming2 */
322         return s->timing[2];
323     case 3: /* LCDTiming3 */
324         return s->timing[3];
325     case 4: /* LCDUPBASE */
326         return s->upbase;
327     case 5: /* LCDLPBASE */
328         return s->lpbase;
329     case 6: /* LCDIMSC */
330         if (s->version != PL110) {
331             return s->cr;
332         }
333         return s->int_mask;
334     case 7: /* LCDControl */
335         if (s->version != PL110) {
336             return s->int_mask;
337         }
338         return s->cr;
339     case 8: /* LCDRIS */
340         return s->int_status;
341     case 9: /* LCDMIS */
342         return s->int_status & s->int_mask;
343     case 11: /* LCDUPCURR */
344         /* TODO: Implement vertical refresh.  */
345         return s->upbase;
346     case 12: /* LCDLPCURR */
347         return s->lpbase;
348     default:
349         hw_error("pl110_read: Bad offset %x\n", (int)offset);
350         return 0;
351     }
352 }
353
354 static void pl110_write(void *opaque, target_phys_addr_t offset,
355                         uint64_t val, unsigned size)
356 {
357     pl110_state *s = (pl110_state *)opaque;
358     int n;
359
360     /* For simplicity invalidate the display whenever a control register
361        is written to.  */
362     s->invalidate = 1;
363     if (offset >= 0x200 && offset < 0x400) {
364         /* Pallette.  */
365         n = (offset - 0x200) >> 2;
366         s->raw_pallette[(offset - 0x200) >> 2] = val;
367         pl110_update_pallette(s, n);
368         return;
369     }
370     switch (offset >> 2) {
371     case 0: /* LCDTiming0 */
372         s->timing[0] = val;
373         n = ((val & 0xfc) + 4) * 4;
374         pl110_resize(s, n, s->rows);
375         break;
376     case 1: /* LCDTiming1 */
377         s->timing[1] = val;
378         n = (val & 0x3ff) + 1;
379         pl110_resize(s, s->cols, n);
380         break;
381     case 2: /* LCDTiming2 */
382         s->timing[2] = val;
383         break;
384     case 3: /* LCDTiming3 */
385         s->timing[3] = val;
386         break;
387     case 4: /* LCDUPBASE */
388         s->upbase = val;
389         break;
390     case 5: /* LCDLPBASE */
391         s->lpbase = val;
392         break;
393     case 6: /* LCDIMSC */
394         if (s->version != PL110) {
395             goto control;
396         }
397     imsc:
398         s->int_mask = val;
399         pl110_update(s);
400         break;
401     case 7: /* LCDControl */
402         if (s->version != PL110) {
403             goto imsc;
404         }
405     control:
406         s->cr = val;
407         s->bpp = (val >> 1) & 7;
408         if (pl110_enabled(s)) {
409             qemu_console_resize(s->ds, s->cols, s->rows);
410         }
411         break;
412     case 10: /* LCDICR */
413         s->int_status &= ~val;
414         pl110_update(s);
415         break;
416     default:
417         hw_error("pl110_write: Bad offset %x\n", (int)offset);
418     }
419 }
420
421 static const MemoryRegionOps pl110_ops = {
422     .read = pl110_read,
423     .write = pl110_write,
424     .endianness = DEVICE_NATIVE_ENDIAN,
425 };
426
427 static void pl110_mux_ctrl_set(void *opaque, int line, int level)
428 {
429     pl110_state *s = (pl110_state *)opaque;
430     s->mux_ctrl = level;
431 }
432
433 static int pl110_init(SysBusDevice *dev)
434 {
435     pl110_state *s = FROM_SYSBUS(pl110_state, dev);
436
437     memory_region_init_io(&s->iomem, &pl110_ops, s, "pl110", 0x1000);
438     sysbus_init_mmio(dev, &s->iomem);
439     sysbus_init_irq(dev, &s->irq);
440     qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
441     s->ds = graphic_console_init(pl110_update_display,
442                                  pl110_invalidate_display,
443                                  NULL, NULL, s);
444     return 0;
445 }
446
447 static int pl110_versatile_init(SysBusDevice *dev)
448 {
449     pl110_state *s = FROM_SYSBUS(pl110_state, dev);
450     s->version = PL110_VERSATILE;
451     return pl110_init(dev);
452 }
453
454 static int pl111_init(SysBusDevice *dev)
455 {
456     pl110_state *s = FROM_SYSBUS(pl110_state, dev);
457     s->version = PL111;
458     return pl110_init(dev);
459 }
460
461 static SysBusDeviceInfo pl110_info = {
462     .init = pl110_init,
463     .qdev.name = "pl110",
464     .qdev.size = sizeof(pl110_state),
465     .qdev.vmsd = &vmstate_pl110,
466     .qdev.no_user = 1,
467 };
468
469 static SysBusDeviceInfo pl110_versatile_info = {
470     .init = pl110_versatile_init,
471     .qdev.name = "pl110_versatile",
472     .qdev.size = sizeof(pl110_state),
473     .qdev.vmsd = &vmstate_pl110,
474     .qdev.no_user = 1,
475 };
476
477 static SysBusDeviceInfo pl111_info = {
478     .init = pl111_init,
479     .qdev.name = "pl111",
480     .qdev.size = sizeof(pl110_state),
481     .qdev.vmsd = &vmstate_pl110,
482     .qdev.no_user = 1,
483 };
484
485 static void pl110_register_devices(void)
486 {
487     sysbus_register_withprop(&pl110_info);
488     sysbus_register_withprop(&pl110_versatile_info);
489     sysbus_register_withprop(&pl111_info);
490 }
491
492 device_init(pl110_register_devices)
This page took 0.051007 seconds and 4 git commands to generate.