Statistics
| Branch: | Revision:

root / hw / pl110.c @ c60e08d9

History | View | Annotate | Download (10.5 kB)

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
    uint32_t base;
32
    DisplayState *ds;
33
    QEMUConsole *console;
34

    
35
    /* The Versatile/PB uses a slightly modified PL110 controller.  */
36
    int versatile;
37
    uint32_t timing[4];
38
    uint32_t cr;
39
    uint32_t upbase;
40
    uint32_t lpbase;
41
    uint32_t int_status;
42
    uint32_t int_mask;
43
    int cols;
44
    int rows;
45
    enum pl110_bppmode bpp;
46
    int invalidate;
47
    uint32_t pallette[256];
48
    uint32_t raw_pallette[128];
49
    qemu_irq irq;
50
} pl110_state;
51

    
52
static const unsigned char pl110_id[] =
53
{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
54

    
55
/* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
56
   has a different ID.  However Linux only looks for the normal ID.  */
57
#if 0
58
static const unsigned char pl110_versatile_id[] =
59
{ 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
60
#else
61
#define pl110_versatile_id pl110_id
62
#endif
63

    
64
static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
65
{
66
    return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
67
}
68

    
69
static inline uint32_t rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
70
{
71
    return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
72
}
73

    
74
static inline uint32_t rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
75
{
76
    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
77
}
78

    
79
static inline uint32_t rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
80
{
81
    return (r << 16) | (g << 8) | b;
82
}
83

    
84
static inline uint32_t rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
85
{
86
    return (r << 16) | (g << 8) | b;
87
}
88

    
89
typedef void (*drawfn)(uint32_t *, uint8_t *, const uint8_t *, int);
90

    
91
#define BITS 8
92
#include "pl110_template.h"
93
#define BITS 15
94
#include "pl110_template.h"
95
#define BITS 16
96
#include "pl110_template.h"
97
#define BITS 24
98
#include "pl110_template.h"
99
#define BITS 32
100
#include "pl110_template.h"
101

    
102
static int pl110_enabled(pl110_state *s)
103
{
104
  return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
105
}
106

    
107
static void pl110_update_display(void *opaque)
108
{
109
    pl110_state *s = (pl110_state *)opaque;
110
    drawfn* fntable;
111
    drawfn fn;
112
    uint32_t *pallette;
113
    uint32_t addr;
114
    uint32_t base;
115
    int dest_width;
116
    int src_width;
117
    uint8_t *dest;
118
    uint8_t *src;
119
    int first, last = 0;
120
    int dirty, new_dirty;
121
    int i;
122
    int bpp_offset;
123

    
124
    if (!pl110_enabled(s))
125
        return;
126

    
127
    switch (s->ds->depth) {
128
    case 0:
129
        return;
130
    case 8:
131
        fntable = pl110_draw_fn_8;
132
        dest_width = 1;
133
        break;
134
    case 15:
135
        fntable = pl110_draw_fn_15;
136
        dest_width = 2;
137
        break;
138
    case 16:
139
        fntable = pl110_draw_fn_16;
140
        dest_width = 2;
141
        break;
142
    case 24:
143
        fntable = pl110_draw_fn_24;
144
        dest_width = 3;
145
        break;
146
    case 32:
147
        fntable = pl110_draw_fn_32;
148
        dest_width = 4;
149
        break;
150
    default:
151
        fprintf(stderr, "pl110: Bad color depth\n");
152
        exit(1);
153
    }
154
    if (s->cr & PL110_CR_BGR)
155
        bpp_offset = 0;
156
    else
157
        bpp_offset = 18;
158

    
159
    if (s->cr & PL110_CR_BEBO)
160
        fn = fntable[s->bpp + 6 + bpp_offset];
161
    else if (s->cr & PL110_CR_BEPO)
162
        fn = fntable[s->bpp + 12 + bpp_offset];
163
    else
164
        fn = fntable[s->bpp + bpp_offset];
165

    
166
    src_width = s->cols;
167
    switch (s->bpp) {
168
    case BPP_1:
169
        src_width >>= 3;
170
        break;
171
    case BPP_2:
172
        src_width >>= 2;
173
        break;
174
    case BPP_4:
175
        src_width >>= 1;
176
        break;
177
    case BPP_8:
178
        break;
179
    case BPP_16:
180
        src_width <<= 1;
181
        break;
182
    case BPP_32:
183
        src_width <<= 2;
184
        break;
185
    }
186
    dest_width *= s->cols;
187
    pallette = s->pallette;
188
    base = s->upbase;
189
    /* HACK: Arm aliases physical memory at 0x80000000.  */
190
    if (base > 0x80000000)
191
        base -= 0x80000000;
192
    src = phys_ram_base + base;
193
    dest = s->ds->data;
194
    first = -1;
195
    addr = base;
196

    
197
    dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
198
    new_dirty = dirty;
199
    for (i = 0; i < s->rows; i++) {
200
        if ((addr & ~TARGET_PAGE_MASK) + src_width >= TARGET_PAGE_SIZE) {
201
            uint32_t tmp;
202
            new_dirty = 0;
203
            for (tmp = 0; tmp < src_width; tmp += TARGET_PAGE_SIZE) {
204
                new_dirty |= cpu_physical_memory_get_dirty(addr + tmp,
205
                                                           VGA_DIRTY_FLAG);
206
            }
207
        }
208

    
209
        if (dirty || new_dirty || s->invalidate) {
210
            fn(pallette, dest, src, s->cols);
211
            if (first == -1)
212
                first = i;
213
            last = i;
214
        }
215
        dirty = new_dirty;
216
        addr += src_width;
217
        dest += dest_width;
218
        src += src_width;
219
    }
220
    if (first < 0)
221
      return;
222

    
223
    s->invalidate = 0;
224
    cpu_physical_memory_reset_dirty(base + first * src_width,
225
                                    base + (last + 1) * src_width,
226
                                    VGA_DIRTY_FLAG);
227
    dpy_update(s->ds, 0, first, s->cols, last - first + 1);
228
}
229

    
230
static void pl110_invalidate_display(void * opaque)
231
{
232
    pl110_state *s = (pl110_state *)opaque;
233
    s->invalidate = 1;
234
}
235

    
236
static void pl110_update_pallette(pl110_state *s, int n)
237
{
238
    int i;
239
    uint32_t raw;
240
    unsigned int r, g, b;
241

    
242
    raw = s->raw_pallette[n];
243
    n <<= 1;
244
    for (i = 0; i < 2; i++) {
245
        r = (raw & 0x1f) << 3;
246
        raw >>= 5;
247
        g = (raw & 0x1f) << 3;
248
        raw >>= 5;
249
        b = (raw & 0x1f) << 3;
250
        /* The I bit is ignored.  */
251
        raw >>= 6;
252
        switch (s->ds->depth) {
253
        case 8:
254
            s->pallette[n] = rgb_to_pixel8(r, g, b);
255
            break;
256
        case 15:
257
            s->pallette[n] = rgb_to_pixel15(r, g, b);
258
            break;
259
        case 16:
260
            s->pallette[n] = rgb_to_pixel16(r, g, b);
261
            break;
262
        case 24:
263
        case 32:
264
            s->pallette[n] = rgb_to_pixel32(r, g, b);
265
            break;
266
        }
267
        n++;
268
    }
269
}
270

    
271
static void pl110_resize(pl110_state *s, int width, int height)
272
{
273
    if (width != s->cols || height != s->rows) {
274
        if (pl110_enabled(s)) {
275
            qemu_console_resize(s->console, width, height);
276
        }
277
    }
278
    s->cols = width;
279
    s->rows = height;
280
}
281

    
282
/* Update interrupts.  */
283
static void pl110_update(pl110_state *s)
284
{
285
  /* TODO: Implement interrupts.  */
286
}
287

    
288
static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
289
{
290
    pl110_state *s = (pl110_state *)opaque;
291

    
292
    offset -= s->base;
293
    if (offset >= 0xfe0 && offset < 0x1000) {
294
        if (s->versatile)
295
            return pl110_versatile_id[(offset - 0xfe0) >> 2];
296
        else
297
            return pl110_id[(offset - 0xfe0) >> 2];
298
    }
299
    if (offset >= 0x200 && offset < 0x400) {
300
        return s->raw_pallette[(offset - 0x200) >> 2];
301
    }
302
    switch (offset >> 2) {
303
    case 0: /* LCDTiming0 */
304
        return s->timing[0];
305
    case 1: /* LCDTiming1 */
306
        return s->timing[1];
307
    case 2: /* LCDTiming2 */
308
        return s->timing[2];
309
    case 3: /* LCDTiming3 */
310
        return s->timing[3];
311
    case 4: /* LCDUPBASE */
312
        return s->upbase;
313
    case 5: /* LCDLPBASE */
314
        return s->lpbase;
315
    case 6: /* LCDIMSC */
316
        if (s->versatile)
317
          return s->cr;
318
        return s->int_mask;
319
    case 7: /* LCDControl */
320
        if (s->versatile)
321
          return s->int_mask;
322
        return s->cr;
323
    case 8: /* LCDRIS */
324
        return s->int_status;
325
    case 9: /* LCDMIS */
326
        return s->int_status & s->int_mask;
327
    case 11: /* LCDUPCURR */
328
        /* TODO: Implement vertical refresh.  */
329
        return s->upbase;
330
    case 12: /* LCDLPCURR */
331
        return s->lpbase;
332
    default:
333
        cpu_abort (cpu_single_env, "pl110_read: Bad offset %x\n", (int)offset);
334
        return 0;
335
    }
336
}
337

    
338
static void pl110_write(void *opaque, target_phys_addr_t offset,
339
                        uint32_t val)
340
{
341
    pl110_state *s = (pl110_state *)opaque;
342
    int n;
343

    
344
    /* For simplicity invalidate the display whenever a control register
345
       is writen to.  */
346
    s->invalidate = 1;
347
    offset -= s->base;
348
    if (offset >= 0x200 && offset < 0x400) {
349
        /* Pallette.  */
350
        n = (offset - 0x200) >> 2;
351
        s->raw_pallette[(offset - 0x200) >> 2] = val;
352
        pl110_update_pallette(s, n);
353
        return;
354
    }
355
    switch (offset >> 2) {
356
    case 0: /* LCDTiming0 */
357
        s->timing[0] = val;
358
        n = ((val & 0xfc) + 4) * 4;
359
        pl110_resize(s, n, s->rows);
360
        break;
361
    case 1: /* LCDTiming1 */
362
        s->timing[1] = val;
363
        n = (val & 0x3ff) + 1;
364
        pl110_resize(s, s->cols, n);
365
        break;
366
    case 2: /* LCDTiming2 */
367
        s->timing[2] = val;
368
        break;
369
    case 3: /* LCDTiming3 */
370
        s->timing[3] = val;
371
        break;
372
    case 4: /* LCDUPBASE */
373
        s->upbase = val;
374
        break;
375
    case 5: /* LCDLPBASE */
376
        s->lpbase = val;
377
        break;
378
    case 6: /* LCDIMSC */
379
        if (s->versatile)
380
            goto control;
381
    imsc:
382
        s->int_mask = val;
383
        pl110_update(s);
384
        break;
385
    case 7: /* LCDControl */
386
        if (s->versatile)
387
            goto imsc;
388
    control:
389
        s->cr = val;
390
        s->bpp = (val >> 1) & 7;
391
        if (pl110_enabled(s)) {
392
            qemu_console_resize(s->console, s->cols, s->rows);
393
        }
394
        break;
395
    case 10: /* LCDICR */
396
        s->int_status &= ~val;
397
        pl110_update(s);
398
        break;
399
    default:
400
        cpu_abort (cpu_single_env, "pl110_write: Bad offset %x\n", (int)offset);
401
    }
402
}
403

    
404
static CPUReadMemoryFunc *pl110_readfn[] = {
405
   pl110_read,
406
   pl110_read,
407
   pl110_read
408
};
409

    
410
static CPUWriteMemoryFunc *pl110_writefn[] = {
411
   pl110_write,
412
   pl110_write,
413
   pl110_write
414
};
415

    
416
void *pl110_init(DisplayState *ds, uint32_t base, qemu_irq irq,
417
                 int versatile)
418
{
419
    pl110_state *s;
420
    int iomemtype;
421

    
422
    s = (pl110_state *)qemu_mallocz(sizeof(pl110_state));
423
    iomemtype = cpu_register_io_memory(0, pl110_readfn,
424
                                       pl110_writefn, s);
425
    cpu_register_physical_memory(base, 0x00001000, iomemtype);
426
    s->base = base;
427
    s->ds = ds;
428
    s->versatile = versatile;
429
    s->irq = irq;
430
    s->console = graphic_console_init(ds, pl110_update_display,
431
                                      pl110_invalidate_display,
432
                                      NULL, NULL, s);
433
    /* ??? Save/restore.  */
434
    return s;
435
}