]> Git Repo - qemu.git/blame - hw/pl110.c
Arm display emulation.
[qemu.git] / hw / pl110.c
CommitLineData
bdd5003a
PB
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
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 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
46static const unsigned char pl110_id[] =
47{ 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
48
49static 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
54static 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
59static 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
64static inline uint32_t rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
65{
66 return (r << 16) | (g << 8) | b;
67}
68
69static inline uint32_t rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
70{
71 return (r << 16) | (g << 8) | b;
72}
73
74typedef 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
87static int pl110_enabled(pl110_state *s)
88{
89 return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
90}
91
92void 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 8:
113 fntable = pl110_draw_fn_8;
114 dest_width = 1;
115 break;
116 case 15:
117 fntable = pl110_draw_fn_15;
118 dest_width = 2;
119 break;
120 case 16:
121 fntable = pl110_draw_fn_16;
122 dest_width = 2;
123 break;
124 case 24:
125 fntable = pl110_draw_fn_24;
126 dest_width = 3;
127 break;
128 case 32:
129 fntable = pl110_draw_fn_32;
130 dest_width = 4;
131 break;
132 default:
133 fprintf(stderr, "qemu: Bad color depth\n");
134 exit(1);
135 }
136 if (s->cr & PL110_CR_BEBO)
137 fn = fntable[s->bpp + 6];
138 else if (s->cr & PL110_CR_BEPO)
139 fn = fntable[s->bpp + 12];
140 else
141 fn = fntable[s->bpp];
142
143 src_width = s->cols;
144 switch (s->bpp) {
145 case BPP_1:
146 src_width >>= 3;
147 break;
148 case BPP_2:
149 src_width >>= 2;
150 break;
151 case BPP_4:
152 src_width >>= 1;
153 break;
154 case BPP_8:
155 break;
156 case BPP_16:
157 src_width <<= 1;
158 break;
159 case BPP_32:
160 src_width <<= 2;
161 break;
162 }
163 dest_width *= s->cols;
164 pallette = s->pallette;
165 base = s->upbase;
166 /* HACK: Arm aliases physical memory at 0x80000000. */
167 if (base > 0x80000000)
168 base -= 0x80000000;
169 src = phys_ram_base + base;
170 dest = s->ds->data;
171 first = -1;
172 addr = base;
173
174 dirty = cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
175 for (i = 0; i < s->rows; i++) {
176 new_dirty = 0;
177 if ((addr & TARGET_PAGE_MASK) + src_width >= TARGET_PAGE_SIZE) {
178 uint32_t tmp;
179 for (tmp = 0; tmp < src_width; tmp += TARGET_PAGE_SIZE) {
180 new_dirty |= cpu_physical_memory_get_dirty(addr + tmp,
181 VGA_DIRTY_FLAG);
182 }
183 }
184
185 if (dirty || new_dirty || s->invalidate) {
186 fn(pallette, dest, src, s->cols);
187 if (first == -1)
188 first = i;
189 last = i;
190 }
191 dirty = new_dirty;
192 addr += src_width;
193 dest += dest_width;
194 src += src_width;
195 }
196 if (first < 0)
197 return;
198
199 s->invalidate = 0;
200 cpu_physical_memory_reset_dirty(base + first * src_width,
201 base + (last + 1) * src_width,
202 VGA_DIRTY_FLAG);
203 dpy_update(s->ds, 0, first, s->cols, last - first + 1);
204}
205
206void pl110_invalidate_display(void * opaque)
207{
208 pl110_state *s = (pl110_state *)opaque;
209 s->invalidate = 1;
210}
211
212static void pl110_update_pallette(pl110_state *s, int n)
213{
214 int i;
215 uint32_t raw;
216 unsigned int r, g, b;
217
218 raw = s->raw_pallette[n];
219 n <<= 1;
220 for (i = 0; i < 2; i++) {
221 r = (raw & 0x1f) << 3;
222 raw >>= 5;
223 g = (raw & 0x1f) << 3;
224 raw >>= 5;
225 b = (raw & 0x1f) << 3;
226 /* The I bit is ignored. */
227 raw >>= 6;
228 switch (s->ds->depth) {
229 case 8:
230 s->pallette[n] = rgb_to_pixel8(r, g, b);
231 break;
232 case 15:
233 s->pallette[n] = rgb_to_pixel15(r, g, b);
234 break;
235 case 16:
236 s->pallette[n] = rgb_to_pixel16(r, g, b);
237 break;
238 case 24:
239 case 32:
240 s->pallette[n] = rgb_to_pixel32(r, g, b);
241 break;
242 }
243 n++;
244 }
245}
246
247static void pl110_resize(pl110_state *s, int width, int height)
248{
249 if (width != s->cols || height != s->rows) {
250 if (pl110_enabled(s)) {
251 dpy_resize(s->ds, s->cols, s->rows);
252 }
253 }
254 s->cols = width;
255 s->rows = height;
256}
257
258/* Update interrupts. */
259static void pl110_update(pl110_state *s)
260{
261 /* TODO: Implement interrupts. */
262}
263
264static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
265{
266 pl110_state *s = (pl110_state *)opaque;
267
268 offset -= s->base;
269 if (offset >= 0xfe0 && offset < 0x1000) {
270 return pl110_id[(offset - 0xfe0) >> 2];
271 }
272 if (offset >= 0x200 && offset < 0x400) {
273 return s->raw_pallette[(offset - 0x200) >> 2];
274 }
275 switch (offset >> 2) {
276 case 0: /* LCDTiming0 */
277 return s->timing[0];
278 case 1: /* LCDTiming1 */
279 return s->timing[1];
280 case 2: /* LCDTiming2 */
281 return s->timing[2];
282 case 3: /* LCDTiming3 */
283 return s->timing[3];
284 case 4: /* LCDUPBASE */
285 return s->upbase;
286 case 5: /* LCDLPBASE */
287 return s->lpbase;
288 case 6: /* LCDIMSC */
289 return s->int_mask;
290 case 7: /* LCDControl */
291 return s->cr;
292 case 8: /* LCDRIS */
293 return s->int_status;
294 case 9: /* LCDMIS */
295 return s->int_status & s->int_mask;
296 case 11: /* LCDUPCURR */
297 /* TODO: Implement vertical refresh. */
298 return s->upbase;
299 case 12: /* LCDLPCURR */
300 return s->lpbase;
301 default:
302 cpu_abort (cpu_single_env, "pl110_read: Bad offset %x\n", offset);
303 return 0;
304 }
305}
306
307static void pl110_write(void *opaque, target_phys_addr_t offset,
308 uint32_t val)
309{
310 pl110_state *s = (pl110_state *)opaque;
311 int n;
312
313 /* For simplicity invalidate the display whenever a control register
314 is writen to. */
315 s->invalidate = 1;
316 offset -= s->base;
317 if (offset >= 0x200 && offset < 0x400) {
318 /* Pallette. */
319 n = (offset - 0x200) >> 2;
320 s->raw_pallette[(offset - 0x200) >> 2] = val;
321 pl110_update_pallette(s, n);
322 }
323 switch (offset >> 2) {
324 case 0: /* LCDTiming0 */
325 s->timing[0] = val;
326 n = ((val & 0xfc) + 4) * 4;
327 pl110_resize(s, n, s->rows);
328 break;
329 case 1: /* LCDTiming1 */
330 s->timing[1] = val;
331 n = (val & 0x3ff) + 1;
332 pl110_resize(s, s->cols, n);
333 break;
334 case 2: /* LCDTiming2 */
335 s->timing[2] = val;
336 break;
337 case 3: /* LCDTiming3 */
338 s->timing[3] = val;
339 break;
340 case 4: /* LCDUPBASE */
341 s->upbase = val;
342 break;
343 case 5: /* LCDLPBASE */
344 s->lpbase = val;
345 break;
346 case 6: /* LCDIMSC */
347 s->int_mask = val;
348 pl110_update(s);
349 break;
350 case 7: /* LCDControl */
351 s->cr = val;
352 s->bpp = (val >> 1) & 7;
353 if (pl110_enabled(s)) {
354 dpy_resize(s->ds, s->cols, s->rows);
355 }
356 break;
357 case 10: /* LCDICR */
358 s->int_status &= ~val;
359 pl110_update(s);
360 break;
361 default:
362 cpu_abort (cpu_single_env, "pl110_write: Bad offset %x\n", offset);
363 }
364}
365
366static CPUReadMemoryFunc *pl110_readfn[] = {
367 pl110_read,
368 pl110_read,
369 pl110_read
370};
371
372static CPUWriteMemoryFunc *pl110_writefn[] = {
373 pl110_write,
374 pl110_write,
375 pl110_write
376};
377
378void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq)
379{
380 pl110_state *s;
381 int iomemtype;
382
383 s = (pl110_state *)qemu_mallocz(sizeof(pl110_state));
384 iomemtype = cpu_register_io_memory(0, pl110_readfn,
385 pl110_writefn, s);
386 cpu_register_physical_memory(base, 0x007fffff, iomemtype);
387 s->base = base;
388 s->ds = ds;
389 s->pic = pic;
390 s->irq = irq;
391 /* ??? Save/restore. */
392 return s;
393}
This page took 0.066759 seconds and 4 git commands to generate.