Statistics
| Branch: | Revision:

root / hw / pl110.c @ c76ee25d

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
401
static CPUReadMemoryFunc *pl110_readfn[] = {
402
   pl110_read,
403
   pl110_read,
404
   pl110_read
405
};
406

    
407
static CPUWriteMemoryFunc *pl110_writefn[] = {
408
   pl110_write,
409
   pl110_write,
410
   pl110_write
411
};
412

    
413
void *pl110_init(DisplayState *ds, uint32_t base, qemu_irq irq,
414
                 int versatile)
415
{
416
    pl110_state *s;
417
    int iomemtype;
418

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