source: trunk/packages/xen-3.1/xen-3.1/tools/ioemu/hw/pl110.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

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