xref: /openbmc/qemu/hw/display/pl110.c (revision 7f709ce7)
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 "qemu/osdep.h"
11 #include "hw/sysbus.h"
12 #include "ui/console.h"
13 #include "framebuffer.h"
14 #include "ui/pixel_ops.h"
15 #include "qemu/log.h"
16 
17 #define PL110_CR_EN   0x001
18 #define PL110_CR_BGR  0x100
19 #define PL110_CR_BEBO 0x200
20 #define PL110_CR_BEPO 0x400
21 #define PL110_CR_PWR  0x800
22 
23 enum pl110_bppmode
24 {
25     BPP_1,
26     BPP_2,
27     BPP_4,
28     BPP_8,
29     BPP_16,
30     BPP_32,
31     BPP_16_565, /* PL111 only */
32     BPP_12      /* PL111 only */
33 };
34 
35 
36 /* The Versatile/PB uses a slightly modified PL110 controller.  */
37 enum pl110_version
38 {
39     PL110,
40     PL110_VERSATILE,
41     PL111
42 };
43 
44 #define TYPE_PL110 "pl110"
45 #define PL110(obj) OBJECT_CHECK(PL110State, (obj), TYPE_PL110)
46 
47 typedef struct PL110State {
48     SysBusDevice parent_obj;
49 
50     MemoryRegion iomem;
51     MemoryRegionSection fbsection;
52     QemuConsole *con;
53 
54     int version;
55     uint32_t timing[4];
56     uint32_t cr;
57     uint32_t upbase;
58     uint32_t lpbase;
59     uint32_t int_status;
60     uint32_t int_mask;
61     int cols;
62     int rows;
63     enum pl110_bppmode bpp;
64     int invalidate;
65     uint32_t mux_ctrl;
66     uint32_t palette[256];
67     uint32_t raw_palette[128];
68     qemu_irq irq;
69 } PL110State;
70 
71 static int vmstate_pl110_post_load(void *opaque, int version_id);
72 
73 static const VMStateDescription vmstate_pl110 = {
74     .name = "pl110",
75     .version_id = 2,
76     .minimum_version_id = 1,
77     .post_load = vmstate_pl110_post_load,
78     .fields = (VMStateField[]) {
79         VMSTATE_INT32(version, PL110State),
80         VMSTATE_UINT32_ARRAY(timing, PL110State, 4),
81         VMSTATE_UINT32(cr, PL110State),
82         VMSTATE_UINT32(upbase, PL110State),
83         VMSTATE_UINT32(lpbase, PL110State),
84         VMSTATE_UINT32(int_status, PL110State),
85         VMSTATE_UINT32(int_mask, PL110State),
86         VMSTATE_INT32(cols, PL110State),
87         VMSTATE_INT32(rows, PL110State),
88         VMSTATE_UINT32(bpp, PL110State),
89         VMSTATE_INT32(invalidate, PL110State),
90         VMSTATE_UINT32_ARRAY(palette, PL110State, 256),
91         VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128),
92         VMSTATE_UINT32_V(mux_ctrl, PL110State, 2),
93         VMSTATE_END_OF_LIST()
94     }
95 };
96 
97 static const unsigned char pl110_id[] =
98 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
99 
100 static const unsigned char pl111_id[] = {
101     0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
102 };
103 
104 
105 /* Indexed by pl110_version */
106 static const unsigned char *idregs[] = {
107     pl110_id,
108     /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board
109      * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware
110      * itself has the same ID values as a stock PL110, and guests (in
111      * particular Linux) rely on this. We emulate what the hardware does,
112      * rather than what the docs claim it ought to do.
113      */
114     pl110_id,
115     pl111_id
116 };
117 
118 #define BITS 8
119 #include "pl110_template.h"
120 #define BITS 15
121 #include "pl110_template.h"
122 #define BITS 16
123 #include "pl110_template.h"
124 #define BITS 24
125 #include "pl110_template.h"
126 #define BITS 32
127 #include "pl110_template.h"
128 
129 static int pl110_enabled(PL110State *s)
130 {
131   return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
132 }
133 
134 static void pl110_update_display(void *opaque)
135 {
136     PL110State *s = (PL110State *)opaque;
137     SysBusDevice *sbd;
138     DisplaySurface *surface = qemu_console_surface(s->con);
139     drawfn* fntable;
140     drawfn fn;
141     int dest_width;
142     int src_width;
143     int bpp_offset;
144     int first;
145     int last;
146 
147     if (!pl110_enabled(s)) {
148         return;
149     }
150 
151     sbd = SYS_BUS_DEVICE(s);
152 
153     switch (surface_bits_per_pixel(surface)) {
154     case 0:
155         return;
156     case 8:
157         fntable = pl110_draw_fn_8;
158         dest_width = 1;
159         break;
160     case 15:
161         fntable = pl110_draw_fn_15;
162         dest_width = 2;
163         break;
164     case 16:
165         fntable = pl110_draw_fn_16;
166         dest_width = 2;
167         break;
168     case 24:
169         fntable = pl110_draw_fn_24;
170         dest_width = 3;
171         break;
172     case 32:
173         fntable = pl110_draw_fn_32;
174         dest_width = 4;
175         break;
176     default:
177         fprintf(stderr, "pl110: Bad color depth\n");
178         exit(1);
179     }
180     if (s->cr & PL110_CR_BGR)
181         bpp_offset = 0;
182     else
183         bpp_offset = 24;
184 
185     if ((s->version != PL111) && (s->bpp == BPP_16)) {
186         /* The PL110's native 16 bit mode is 5551; however
187          * most boards with a PL110 implement an external
188          * mux which allows bits to be reshuffled to give
189          * 565 format. The mux is typically controlled by
190          * an external system register.
191          * This is controlled by a GPIO input pin
192          * so boards can wire it up to their register.
193          *
194          * The PL111 straightforwardly implements both
195          * 5551 and 565 under control of the bpp field
196          * in the LCDControl register.
197          */
198         switch (s->mux_ctrl) {
199         case 3: /* 565 BGR */
200             bpp_offset = (BPP_16_565 - BPP_16);
201             break;
202         case 1: /* 5551 */
203             break;
204         case 0: /* 888; also if we have loaded vmstate from an old version */
205         case 2: /* 565 RGB */
206         default:
207             /* treat as 565 but honour BGR bit */
208             bpp_offset += (BPP_16_565 - BPP_16);
209             break;
210         }
211     }
212 
213     if (s->cr & PL110_CR_BEBO)
214         fn = fntable[s->bpp + 8 + bpp_offset];
215     else if (s->cr & PL110_CR_BEPO)
216         fn = fntable[s->bpp + 16 + bpp_offset];
217     else
218         fn = fntable[s->bpp + bpp_offset];
219 
220     src_width = s->cols;
221     switch (s->bpp) {
222     case BPP_1:
223         src_width >>= 3;
224         break;
225     case BPP_2:
226         src_width >>= 2;
227         break;
228     case BPP_4:
229         src_width >>= 1;
230         break;
231     case BPP_8:
232         break;
233     case BPP_16:
234     case BPP_16_565:
235     case BPP_12:
236         src_width <<= 1;
237         break;
238     case BPP_32:
239         src_width <<= 2;
240         break;
241     }
242     dest_width *= s->cols;
243     first = 0;
244     if (s->invalidate) {
245         framebuffer_update_memory_section(&s->fbsection,
246                                           sysbus_address_space(sbd),
247                                           s->upbase,
248                                           s->rows, src_width);
249     }
250 
251     framebuffer_update_display(surface, &s->fbsection,
252                                s->cols, s->rows,
253                                src_width, dest_width, 0,
254                                s->invalidate,
255                                fn, s->palette,
256                                &first, &last);
257 
258     if (first >= 0) {
259         dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
260     }
261     s->invalidate = 0;
262 }
263 
264 static void pl110_invalidate_display(void * opaque)
265 {
266     PL110State *s = (PL110State *)opaque;
267     s->invalidate = 1;
268     if (pl110_enabled(s)) {
269         qemu_console_resize(s->con, s->cols, s->rows);
270     }
271 }
272 
273 static void pl110_update_palette(PL110State *s, int n)
274 {
275     DisplaySurface *surface = qemu_console_surface(s->con);
276     int i;
277     uint32_t raw;
278     unsigned int r, g, b;
279 
280     raw = s->raw_palette[n];
281     n <<= 1;
282     for (i = 0; i < 2; i++) {
283         r = (raw & 0x1f) << 3;
284         raw >>= 5;
285         g = (raw & 0x1f) << 3;
286         raw >>= 5;
287         b = (raw & 0x1f) << 3;
288         /* The I bit is ignored.  */
289         raw >>= 6;
290         switch (surface_bits_per_pixel(surface)) {
291         case 8:
292             s->palette[n] = rgb_to_pixel8(r, g, b);
293             break;
294         case 15:
295             s->palette[n] = rgb_to_pixel15(r, g, b);
296             break;
297         case 16:
298             s->palette[n] = rgb_to_pixel16(r, g, b);
299             break;
300         case 24:
301         case 32:
302             s->palette[n] = rgb_to_pixel32(r, g, b);
303             break;
304         }
305         n++;
306     }
307 }
308 
309 static void pl110_resize(PL110State *s, int width, int height)
310 {
311     if (width != s->cols || height != s->rows) {
312         if (pl110_enabled(s)) {
313             qemu_console_resize(s->con, width, height);
314         }
315     }
316     s->cols = width;
317     s->rows = height;
318 }
319 
320 /* Update interrupts.  */
321 static void pl110_update(PL110State *s)
322 {
323   /* TODO: Implement interrupts.  */
324 }
325 
326 static uint64_t pl110_read(void *opaque, hwaddr offset,
327                            unsigned size)
328 {
329     PL110State *s = (PL110State *)opaque;
330 
331     if (offset >= 0xfe0 && offset < 0x1000) {
332         return idregs[s->version][(offset - 0xfe0) >> 2];
333     }
334     if (offset >= 0x200 && offset < 0x400) {
335         return s->raw_palette[(offset - 0x200) >> 2];
336     }
337     switch (offset >> 2) {
338     case 0: /* LCDTiming0 */
339         return s->timing[0];
340     case 1: /* LCDTiming1 */
341         return s->timing[1];
342     case 2: /* LCDTiming2 */
343         return s->timing[2];
344     case 3: /* LCDTiming3 */
345         return s->timing[3];
346     case 4: /* LCDUPBASE */
347         return s->upbase;
348     case 5: /* LCDLPBASE */
349         return s->lpbase;
350     case 6: /* LCDIMSC */
351         if (s->version != PL110) {
352             return s->cr;
353         }
354         return s->int_mask;
355     case 7: /* LCDControl */
356         if (s->version != PL110) {
357             return s->int_mask;
358         }
359         return s->cr;
360     case 8: /* LCDRIS */
361         return s->int_status;
362     case 9: /* LCDMIS */
363         return s->int_status & s->int_mask;
364     case 11: /* LCDUPCURR */
365         /* TODO: Implement vertical refresh.  */
366         return s->upbase;
367     case 12: /* LCDLPCURR */
368         return s->lpbase;
369     default:
370         qemu_log_mask(LOG_GUEST_ERROR,
371                       "pl110_read: Bad offset %x\n", (int)offset);
372         return 0;
373     }
374 }
375 
376 static void pl110_write(void *opaque, hwaddr offset,
377                         uint64_t val, unsigned size)
378 {
379     PL110State *s = (PL110State *)opaque;
380     int n;
381 
382     /* For simplicity invalidate the display whenever a control register
383        is written to.  */
384     s->invalidate = 1;
385     if (offset >= 0x200 && offset < 0x400) {
386         /* Palette.  */
387         n = (offset - 0x200) >> 2;
388         s->raw_palette[(offset - 0x200) >> 2] = val;
389         pl110_update_palette(s, n);
390         return;
391     }
392     switch (offset >> 2) {
393     case 0: /* LCDTiming0 */
394         s->timing[0] = val;
395         n = ((val & 0xfc) + 4) * 4;
396         pl110_resize(s, n, s->rows);
397         break;
398     case 1: /* LCDTiming1 */
399         s->timing[1] = val;
400         n = (val & 0x3ff) + 1;
401         pl110_resize(s, s->cols, n);
402         break;
403     case 2: /* LCDTiming2 */
404         s->timing[2] = val;
405         break;
406     case 3: /* LCDTiming3 */
407         s->timing[3] = val;
408         break;
409     case 4: /* LCDUPBASE */
410         s->upbase = val;
411         break;
412     case 5: /* LCDLPBASE */
413         s->lpbase = val;
414         break;
415     case 6: /* LCDIMSC */
416         if (s->version != PL110) {
417             goto control;
418         }
419     imsc:
420         s->int_mask = val;
421         pl110_update(s);
422         break;
423     case 7: /* LCDControl */
424         if (s->version != PL110) {
425             goto imsc;
426         }
427     control:
428         s->cr = val;
429         s->bpp = (val >> 1) & 7;
430         if (pl110_enabled(s)) {
431             qemu_console_resize(s->con, s->cols, s->rows);
432         }
433         break;
434     case 10: /* LCDICR */
435         s->int_status &= ~val;
436         pl110_update(s);
437         break;
438     default:
439         qemu_log_mask(LOG_GUEST_ERROR,
440                       "pl110_write: Bad offset %x\n", (int)offset);
441     }
442 }
443 
444 static const MemoryRegionOps pl110_ops = {
445     .read = pl110_read,
446     .write = pl110_write,
447     .endianness = DEVICE_NATIVE_ENDIAN,
448 };
449 
450 static void pl110_mux_ctrl_set(void *opaque, int line, int level)
451 {
452     PL110State *s = (PL110State *)opaque;
453     s->mux_ctrl = level;
454 }
455 
456 static int vmstate_pl110_post_load(void *opaque, int version_id)
457 {
458     PL110State *s = opaque;
459     /* Make sure we redraw, and at the right size */
460     pl110_invalidate_display(s);
461     return 0;
462 }
463 
464 static const GraphicHwOps pl110_gfx_ops = {
465     .invalidate  = pl110_invalidate_display,
466     .gfx_update  = pl110_update_display,
467 };
468 
469 static void pl110_realize(DeviceState *dev, Error **errp)
470 {
471     PL110State *s = PL110(dev);
472     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
473 
474     memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
475     sysbus_init_mmio(sbd, &s->iomem);
476     sysbus_init_irq(sbd, &s->irq);
477     qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
478     s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
479 }
480 
481 static void pl110_init(Object *obj)
482 {
483     PL110State *s = PL110(obj);
484 
485     s->version = PL110;
486 }
487 
488 static void pl110_versatile_init(Object *obj)
489 {
490     PL110State *s = PL110(obj);
491 
492     s->version = PL110_VERSATILE;
493 }
494 
495 static void pl111_init(Object *obj)
496 {
497     PL110State *s = PL110(obj);
498 
499     s->version = PL111;
500 }
501 
502 static void pl110_class_init(ObjectClass *klass, void *data)
503 {
504     DeviceClass *dc = DEVICE_CLASS(klass);
505 
506     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
507     dc->vmsd = &vmstate_pl110;
508     dc->realize = pl110_realize;
509 }
510 
511 static const TypeInfo pl110_info = {
512     .name          = TYPE_PL110,
513     .parent        = TYPE_SYS_BUS_DEVICE,
514     .instance_size = sizeof(PL110State),
515     .instance_init = pl110_init,
516     .class_init    = pl110_class_init,
517 };
518 
519 static const TypeInfo pl110_versatile_info = {
520     .name          = "pl110_versatile",
521     .parent        = TYPE_PL110,
522     .instance_init = pl110_versatile_init,
523 };
524 
525 static const TypeInfo pl111_info = {
526     .name          = "pl111",
527     .parent        = TYPE_PL110,
528     .instance_init = pl111_init,
529 };
530 
531 static void pl110_register_types(void)
532 {
533     type_register_static(&pl110_info);
534     type_register_static(&pl110_versatile_info);
535     type_register_static(&pl111_info);
536 }
537 
538 type_init(pl110_register_types)
539