7ce74a2f671f6e187dda44059620dacf1a6c050c
[qemu] / hw / pl110.c
1 /*
2  * Arm PrimeCell PL110 Color LCD Controller
3  *
4  * Copyright (c) 2005-2006 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licenced under the GNU LGPL
8  */
9
10 #include "hw.h"
11 #include "primecell.h"
12 #include "console.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 };
29
30 typedef struct {
31     DisplayState *ds;
32
33     /* The Versatile/PB uses a slightly modified PL110 controller.  */
34     int versatile;
35     uint32_t timing[4];
36     uint32_t cr;
37     uint32_t upbase;
38     uint32_t lpbase;
39     uint32_t int_status;
40     uint32_t int_mask;
41     int cols;
42     int rows;
43     enum pl110_bppmode bpp;
44     int invalidate;
45     uint32_t pallette[256];
46     uint32_t raw_pallette[128];
47     qemu_irq irq;
48 } pl110_state;
49
50 static const unsigned char pl110_id[] =
51 { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
52
53 /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
54    has a different ID.  However Linux only looks for the normal ID.  */
55 #if 0
56 static const unsigned char pl110_versatile_id[] =
57 { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
58 #else
59 #define pl110_versatile_id pl110_id
60 #endif
61
62 #include "pixel_ops.h"
63
64 typedef void (*drawfn)(uint32_t *, uint8_t *, const uint8_t *, int);
65
66 #define BITS 8
67 #include "pl110_template.h"
68 #define BITS 15
69 #include "pl110_template.h"
70 #define BITS 16
71 #include "pl110_template.h"
72 #define BITS 24
73 #include "pl110_template.h"
74 #define BITS 32
75 #include "pl110_template.h"
76
77 static int pl110_enabled(pl110_state *s)
78 {
79   return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
80 }
81
82 static void pl110_update_display(void *opaque)
83 {
84     pl110_state *s = (pl110_state *)opaque;
85     drawfn* fntable;
86     drawfn fn;
87     uint32_t *pallette;
88     uint32_t addr;
89     uint32_t base;
90     int dest_width;
91     int src_width;
92     uint8_t *dest;
93     uint8_t *src;
94     int first, last = 0;
95     int dirty, new_dirty;
96     int i;
97     int bpp_offset;
98
99     if (!pl110_enabled(s))
100         return;
101
102     switch (ds_get_bits_per_pixel(s->ds)) {
103     case 0:
104         return;
105     case 8:
106         fntable = pl110_draw_fn_8;
107         dest_width = 1;
108         break;
109     case 15:
110         fntable = pl110_draw_fn_15;
111         dest_width = 2;
112         break;
113     case 16:
114         fntable = pl110_draw_fn_16;
115         dest_width = 2;
116         break;
117     case 24:
118         fntable = pl110_draw_fn_24;
119         dest_width = 3;
120         break;
121     case 32:
122         fntable = pl110_draw_fn_32;
123         dest_width = 4;
124         break;
125     default:
126         fprintf(stderr, "pl110: Bad color depth\n");
127         exit(1);
128     }
129     if (s->cr & PL110_CR_BGR)
130         bpp_offset = 0;
131     else
132         bpp_offset = 18;
133
134     if (s->cr & PL110_CR_BEBO)
135         fn = fntable[s->bpp + 6 + bpp_offset];
136     else if (s->cr & PL110_CR_BEPO)
137         fn = fntable[s->bpp + 12 + bpp_offset];
138     else
139         fn = fntable[s->bpp + bpp_offset];
140
141     src_width = s->cols;
142     switch (s->bpp) {
143     case BPP_1:
144         src_width >>= 3;
145         break;
146     case BPP_2:
147         src_width >>= 2;
148         break;
149     case BPP_4:
150         src_width >>= 1;
151         break;
152     case BPP_8:
153         break;
154     case BPP_16:
155         src_width <<= 1;
156         break;
157     case BPP_32:
158         src_width <<= 2;
159         break;
160     }
161     dest_width *= s->cols;
162     pallette = s->pallette;
163     base = s->upbase;
164     /* HACK: Arm aliases physical memory at 0x80000000.  */
165     if (base > 0x80000000)
166         base -= 0x80000000;
167     src = phys_ram_base + base;
168     dest = ds_get_data(s->ds);
169     first = -1;
170     addr = base;
171
172     dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
173     new_dirty = dirty;
174     for (i = 0; i < s->rows; i++) {
175         if ((addr & ~TARGET_PAGE_MASK) + src_width >= TARGET_PAGE_SIZE) {
176             uint32_t tmp;
177             new_dirty = 0;
178             for (tmp = 0; tmp < src_width; tmp += TARGET_PAGE_SIZE) {
179                 new_dirty |= cpu_physical_memory_get_dirty(addr + tmp,
180                                                            VGA_DIRTY_FLAG);
181             }
182         }
183
184         if (dirty || new_dirty || s->invalidate) {
185             fn(pallette, dest, src, s->cols);
186             if (first == -1)
187                 first = i;
188             last = i;
189         }
190         dirty = new_dirty;
191         addr += src_width;
192         dest += dest_width;
193         src += src_width;
194     }
195     if (first < 0)
196       return;
197
198     s->invalidate = 0;
199     cpu_physical_memory_reset_dirty(base + first * src_width,
200                                     base + (last + 1) * src_width,
201                                     VGA_DIRTY_FLAG);
202     dpy_update(s->ds, 0, first, s->cols, last - first + 1);
203 }
204
205 static void pl110_invalidate_display(void * opaque)
206 {
207     pl110_state *s = (pl110_state *)opaque;
208     s->invalidate = 1;
209 }
210
211 static void pl110_update_pallette(pl110_state *s, int n)
212 {
213     int i;
214     uint32_t raw;
215     unsigned int r, g, b;
216
217     raw = s->raw_pallette[n];
218     n <<= 1;
219     for (i = 0; i < 2; i++) {
220         r = (raw & 0x1f) << 3;
221         raw >>= 5;
222         g = (raw & 0x1f) << 3;
223         raw >>= 5;
224         b = (raw & 0x1f) << 3;
225         /* The I bit is ignored.  */
226         raw >>= 6;
227         switch (ds_get_bits_per_pixel(s->ds)) {
228         case 8:
229             s->pallette[n] = rgb_to_pixel8(r, g, b);
230             break;
231         case 15:
232             s->pallette[n] = rgb_to_pixel15(r, g, b);
233             break;
234         case 16:
235             s->pallette[n] = rgb_to_pixel16(r, g, b);
236             break;
237         case 24:
238         case 32:
239             s->pallette[n] = rgb_to_pixel32(r, g, b);
240             break;
241         }
242         n++;
243     }
244 }
245
246 static void pl110_resize(pl110_state *s, int width, int height)
247 {
248     if (width != s->cols || height != s->rows) {
249         if (pl110_enabled(s)) {
250             qemu_console_resize(s->ds, width, height);
251         }
252     }
253     s->cols = width;
254     s->rows = height;
255 }
256
257 /* Update interrupts.  */
258 static void pl110_update(pl110_state *s)
259 {
260   /* TODO: Implement interrupts.  */
261 }
262
263 static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
264 {
265     pl110_state *s = (pl110_state *)opaque;
266
267     if (offset >= 0xfe0 && offset < 0x1000) {
268         if (s->versatile)
269             return pl110_versatile_id[(offset - 0xfe0) >> 2];
270         else
271             return pl110_id[(offset - 0xfe0) >> 2];
272     }
273     if (offset >= 0x200 && offset < 0x400) {
274         return s->raw_pallette[(offset - 0x200) >> 2];
275     }
276     switch (offset >> 2) {
277     case 0: /* LCDTiming0 */
278         return s->timing[0];
279     case 1: /* LCDTiming1 */
280         return s->timing[1];
281     case 2: /* LCDTiming2 */
282         return s->timing[2];
283     case 3: /* LCDTiming3 */
284         return s->timing[3];
285     case 4: /* LCDUPBASE */
286         return s->upbase;
287     case 5: /* LCDLPBASE */
288         return s->lpbase;
289     case 6: /* LCDIMSC */
290         if (s->versatile)
291           return s->cr;
292         return s->int_mask;
293     case 7: /* LCDControl */
294         if (s->versatile)
295           return s->int_mask;
296         return s->cr;
297     case 8: /* LCDRIS */
298         return s->int_status;
299     case 9: /* LCDMIS */
300         return s->int_status & s->int_mask;
301     case 11: /* LCDUPCURR */
302         /* TODO: Implement vertical refresh.  */
303         return s->upbase;
304     case 12: /* LCDLPCURR */
305         return s->lpbase;
306     default:
307         cpu_abort (cpu_single_env, "pl110_read: Bad offset %x\n", (int)offset);
308         return 0;
309     }
310 }
311
312 static void pl110_write(void *opaque, target_phys_addr_t offset,
313                         uint32_t val)
314 {
315     pl110_state *s = (pl110_state *)opaque;
316     int n;
317
318     /* For simplicity invalidate the display whenever a control register
319        is writen to.  */
320     s->invalidate = 1;
321     if (offset >= 0x200 && offset < 0x400) {
322         /* Pallette.  */
323         n = (offset - 0x200) >> 2;
324         s->raw_pallette[(offset - 0x200) >> 2] = val;
325         pl110_update_pallette(s, n);
326         return;
327     }
328     switch (offset >> 2) {
329     case 0: /* LCDTiming0 */
330         s->timing[0] = val;
331         n = ((val & 0xfc) + 4) * 4;
332         pl110_resize(s, n, s->rows);
333         break;
334     case 1: /* LCDTiming1 */
335         s->timing[1] = val;
336         n = (val & 0x3ff) + 1;
337         pl110_resize(s, s->cols, n);
338         break;
339     case 2: /* LCDTiming2 */
340         s->timing[2] = val;
341         break;
342     case 3: /* LCDTiming3 */
343         s->timing[3] = val;
344         break;
345     case 4: /* LCDUPBASE */
346         s->upbase = val;
347         break;
348     case 5: /* LCDLPBASE */
349         s->lpbase = val;
350         break;
351     case 6: /* LCDIMSC */
352         if (s->versatile)
353             goto control;
354     imsc:
355         s->int_mask = val;
356         pl110_update(s);
357         break;
358     case 7: /* LCDControl */
359         if (s->versatile)
360             goto imsc;
361     control:
362         s->cr = val;
363         s->bpp = (val >> 1) & 7;
364         if (pl110_enabled(s)) {
365             qemu_console_resize(s->ds, s->cols, s->rows);
366         }
367         break;
368     case 10: /* LCDICR */
369         s->int_status &= ~val;
370         pl110_update(s);
371         break;
372     default:
373         cpu_abort (cpu_single_env, "pl110_write: Bad offset %x\n", (int)offset);
374     }
375 }
376
377 static CPUReadMemoryFunc *pl110_readfn[] = {
378    pl110_read,
379    pl110_read,
380    pl110_read
381 };
382
383 static CPUWriteMemoryFunc *pl110_writefn[] = {
384    pl110_write,
385    pl110_write,
386    pl110_write
387 };
388
389 void *pl110_init(uint32_t base, qemu_irq irq, int versatile)
390 {
391     pl110_state *s;
392     int iomemtype;
393
394     s = (pl110_state *)qemu_mallocz(sizeof(pl110_state));
395     iomemtype = cpu_register_io_memory(0, pl110_readfn,
396                                        pl110_writefn, s);
397     cpu_register_physical_memory(base, 0x00001000, iomemtype);
398     s->versatile = versatile;
399     s->irq = irq;
400     s->ds = graphic_console_init(pl110_update_display,
401                                  pl110_invalidate_display,
402                                  NULL, NULL, s);
403     /* ??? Save/restore.  */
404     return s;
405 }