Statistics
| Branch: | Revision:

root / hw / pl110.c @ 95219897

History | View | Annotate | Download (9.5 kB)

1
/* 
2
 * Arm PrimeCell PL110 Color LCD Controller
3
 *
4
 * Copyright (c) 2005 CodeSourcery, LLC.
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

    
17
enum pl110_bppmode
18
{
19
    BPP_1,
20
    BPP_2,
21
    BPP_4,
22
    BPP_8,
23
    BPP_16,
24
    BPP_32
25
};
26

    
27
typedef struct {
28
    uint32_t base;
29
    DisplayState *ds;
30
    void *pic;
31
    uint32_t timing[4];
32
    uint32_t cr;
33
    uint32_t upbase;
34
    uint32_t lpbase;
35
    uint32_t int_status;
36
    uint32_t int_mask;
37
    int cols;
38
    int rows;
39
    enum pl110_bppmode bpp;
40
    int invalidate;
41
    uint32_t pallette[256];
42
    uint32_t raw_pallette[128];
43
    int irq;
44
} pl110_state;
45

    
46
static const unsigned char pl110_id[] =
47
{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
48

    
49
static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
50
{
51
    return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
52
}
53

    
54
static inline uint32_t rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
55
{
56
    return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
57
}
58

    
59
static inline uint32_t rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
60
{
61
    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
62
}
63

    
64
static inline uint32_t rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
65
{
66
    return (r << 16) | (g << 8) | b;
67
}
68

    
69
static inline uint32_t rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
70
{
71
    return (r << 16) | (g << 8) | b;
72
}
73

    
74
typedef void (*drawfn)(uint32_t *, uint8_t *, const uint8_t *, int);
75

    
76
#define BITS 8
77
#include "pl110_template.h"
78
#define BITS 15
79
#include "pl110_template.h"
80
#define BITS 16
81
#include "pl110_template.h"
82
#define BITS 24
83
#include "pl110_template.h"
84
#define BITS 32
85
#include "pl110_template.h"
86

    
87
static int pl110_enabled(pl110_state *s)
88
{
89
  return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
90
}
91

    
92
static void pl110_update_display(void *opaque)
93
{
94
    pl110_state *s = (pl110_state *)opaque;
95
    drawfn* fntable;
96
    drawfn fn;
97
    uint32_t *pallette;
98
    uint32_t addr;
99
    uint32_t base;
100
    int dest_width;
101
    int src_width;
102
    uint8_t *dest;
103
    uint8_t *src;
104
    int first, last;
105
    int dirty, new_dirty;
106
    int i;
107

    
108
    if (!pl110_enabled(s))
109
        return;
110
    
111
    switch (s->ds->depth) {
112
    case 0:
113
        return;
114
    case 8:
115
        fntable = pl110_draw_fn_8;
116
        dest_width = 1;
117
        break;
118
    case 15:
119
        fntable = pl110_draw_fn_15;
120
        dest_width = 2;
121
        break;
122
    case 16:
123
        fntable = pl110_draw_fn_16;
124
        dest_width = 2;
125
        break;
126
    case 24:
127
        fntable = pl110_draw_fn_24;
128
        dest_width = 3;
129
        break;
130
    case 32:
131
        fntable = pl110_draw_fn_32;
132
        dest_width = 4;
133
        break;
134
    default:
135
        fprintf(stderr, "pl110: Bad color depth\n");
136
        exit(1);
137
    }
138
    if (s->cr & PL110_CR_BEBO)
139
      fn = fntable[s->bpp + 6];
140
    else if (s->cr & PL110_CR_BEPO)
141
      fn = fntable[s->bpp + 12];
142
    else
143
      fn = fntable[s->bpp];
144
    
145
    src_width = s->cols;
146
    switch (s->bpp) {
147
    case BPP_1:
148
        src_width >>= 3;
149
        break;
150
    case BPP_2:
151
        src_width >>= 2;
152
        break;
153
    case BPP_4:
154
        src_width >>= 1;
155
        break;
156
    case BPP_8:
157
        break;
158
    case BPP_16:
159
        src_width <<= 1;
160
        break;
161
    case BPP_32:
162
        src_width <<= 2;
163
        break;
164
    }
165
    dest_width *= s->cols;
166
    pallette = s->pallette;
167
    base = s->upbase;
168
    /* HACK: Arm aliases physical memory at 0x80000000.  */
169
    if (base > 0x80000000)
170
        base -= 0x80000000;
171
    src = phys_ram_base + base;
172
    dest = s->ds->data;
173
    first = -1;
174
    addr = base;
175

    
176
    dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
177
    for (i = 0; i < s->rows; i++) {
178
        new_dirty = 0;
179
        if ((addr & TARGET_PAGE_MASK) + src_width >= TARGET_PAGE_SIZE) {
180
            uint32_t tmp;
181
            for (tmp = 0; tmp < src_width; tmp += TARGET_PAGE_SIZE) {
182
                new_dirty |= cpu_physical_memory_get_dirty(addr + tmp,
183
                                                           VGA_DIRTY_FLAG);
184
            }
185
        }
186

    
187
        if (dirty || new_dirty || s->invalidate) {
188
            fn(pallette, dest, src, s->cols);
189
            if (first == -1)
190
                first = i;
191
            last = i;
192
        }
193
        dirty = new_dirty;
194
        addr += src_width;
195
        dest += dest_width;
196
        src += src_width;
197
    }
198
    if (first < 0)
199
      return;
200

    
201
    s->invalidate = 0;
202
    cpu_physical_memory_reset_dirty(base + first * src_width,
203
                                    base + (last + 1) * src_width,
204
                                    VGA_DIRTY_FLAG);
205
    dpy_update(s->ds, 0, first, s->cols, last - first + 1);
206
}
207

    
208
static void pl110_invalidate_display(void * opaque)
209
{
210
    pl110_state *s = (pl110_state *)opaque;
211
    s->invalidate = 1;
212
}
213

    
214
static void pl110_update_pallette(pl110_state *s, int n)
215
{
216
    int i;
217
    uint32_t raw;
218
    unsigned int r, g, b;
219

    
220
    raw = s->raw_pallette[n];
221
    n <<= 1;
222
    for (i = 0; i < 2; i++) {
223
        r = (raw & 0x1f) << 3;
224
        raw >>= 5;
225
        g = (raw & 0x1f) << 3;
226
        raw >>= 5;
227
        b = (raw & 0x1f) << 3;
228
        /* The I bit is ignored.  */
229
        raw >>= 6;
230
        switch (s->ds->depth) {
231
        case 8:
232
            s->pallette[n] = rgb_to_pixel8(r, g, b);
233
            break;
234
        case 15:
235
            s->pallette[n] = rgb_to_pixel15(r, g, b);
236
            break;
237
        case 16:
238
            s->pallette[n] = rgb_to_pixel16(r, g, b);
239
            break;
240
        case 24:
241
        case 32:
242
            s->pallette[n] = rgb_to_pixel32(r, g, b);
243
            break;
244
        }
245
        n++;
246
    }
247
}
248

    
249
static void pl110_resize(pl110_state *s, int width, int height)
250
{
251
    if (width != s->cols || height != s->rows) {
252
        if (pl110_enabled(s)) {
253
            dpy_resize(s->ds, s->cols, s->rows);
254
        }
255
    }
256
    s->cols = width;
257
    s->rows = height;
258
}
259

    
260
/* Update interrupts.  */
261
static void pl110_update(pl110_state *s)
262
{
263
  /* TODO: Implement interrupts.  */
264
}
265

    
266
static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
267
{
268
    pl110_state *s = (pl110_state *)opaque;
269

    
270
    offset -= s->base;
271
    if (offset >= 0xfe0 && offset < 0x1000) {
272
        return pl110_id[(offset - 0xfe0) >> 2];
273
    }
274
    if (offset >= 0x200 && offset < 0x400) {
275
        return s->raw_pallette[(offset - 0x200) >> 2];
276
    }
277
    switch (offset >> 2) {
278
    case 0: /* LCDTiming0 */
279
        return s->timing[0];
280
    case 1: /* LCDTiming1 */
281
        return s->timing[1];
282
    case 2: /* LCDTiming2 */
283
        return s->timing[2];
284
    case 3: /* LCDTiming3 */
285
        return s->timing[3];
286
    case 4: /* LCDUPBASE */
287
        return s->upbase;
288
    case 5: /* LCDLPBASE */
289
        return s->lpbase;
290
    case 6: /* LCDIMSC */
291
        return s->int_mask;
292
    case 7: /* LCDControl */
293
        return s->cr;
294
    case 8: /* LCDRIS */
295
        return s->int_status;
296
    case 9: /* LCDMIS */
297
        return s->int_status & s->int_mask;
298
    case 11: /* LCDUPCURR */
299
        /* TODO: Implement vertical refresh.  */
300
        return s->upbase;
301
    case 12: /* LCDLPCURR */
302
        return s->lpbase;
303
    default:
304
        cpu_abort (cpu_single_env, "pl110_read: Bad offset %x\n", offset);
305
        return 0;
306
    }
307
}
308

    
309
static void pl110_write(void *opaque, target_phys_addr_t offset,
310
                        uint32_t val)
311
{
312
    pl110_state *s = (pl110_state *)opaque;
313
    int n;
314

    
315
    /* For simplicity invalidate the display whenever a control register
316
       is writen to.  */
317
    s->invalidate = 1;
318
    offset -= s->base;
319
    if (offset >= 0x200 && offset < 0x400) {
320
        /* Pallette.  */
321
        n = (offset - 0x200) >> 2;
322
        s->raw_pallette[(offset - 0x200) >> 2] = val;
323
        pl110_update_pallette(s, n);
324
        return;
325
    }
326
    switch (offset >> 2) {
327
    case 0: /* LCDTiming0 */
328
        s->timing[0] = val;
329
        n = ((val & 0xfc) + 4) * 4;
330
        pl110_resize(s, n, s->rows);
331
        break;
332
    case 1: /* LCDTiming1 */
333
        s->timing[1] = val;
334
        n = (val & 0x3ff) + 1;
335
        pl110_resize(s, s->cols, n);
336
        break;
337
    case 2: /* LCDTiming2 */
338
        s->timing[2] = val;
339
        break;
340
    case 3: /* LCDTiming3 */
341
        s->timing[3] = val;
342
        break;
343
    case 4: /* LCDUPBASE */
344
        s->upbase = val;
345
        break;
346
    case 5: /* LCDLPBASE */
347
        s->lpbase = val;
348
        break;
349
    case 6: /* LCDIMSC */
350
        s->int_mask = val;
351
        pl110_update(s);
352
        break;
353
    case 7: /* LCDControl */
354
        s->cr = val;
355
        s->bpp = (val >> 1) & 7;
356
        if (pl110_enabled(s)) {
357
            dpy_resize(s->ds, s->cols, s->rows);
358
        }
359
        break;
360
    case 10: /* LCDICR */
361
        s->int_status &= ~val;
362
        pl110_update(s);
363
        break;
364
    default:
365
        cpu_abort (cpu_single_env, "pl110_write: Bad offset %x\n", offset);
366
    }
367
}
368

    
369
static CPUReadMemoryFunc *pl110_readfn[] = {
370
   pl110_read,
371
   pl110_read,
372
   pl110_read
373
};
374

    
375
static CPUWriteMemoryFunc *pl110_writefn[] = {
376
   pl110_write,
377
   pl110_write,
378
   pl110_write
379
};
380

    
381
void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq,
382
                 int versatile)
383
{
384
    pl110_state *s;
385
    int iomemtype;
386

    
387
    s = (pl110_state *)qemu_mallocz(sizeof(pl110_state));
388
    iomemtype = cpu_register_io_memory(0, pl110_readfn,
389
                                       pl110_writefn, s);
390
    cpu_register_physical_memory(base, 0x00000fff, iomemtype);
391
    s->base = base;
392
    s->ds = ds;
393
    s->pic = pic;
394
    s->irq = irq;
395
    graphic_console_init(ds, pl110_update_display, pl110_invalidate_display,
396
                         NULL, s);
397
    /* ??? Save/restore.  */
398
    return s;
399
}