Add rotation of the screen with portrait option.
[qemu/mini2440.git] / hw / s3c24xx_lcd.c
blobf1520da3015d40aa5719090b2d08a857b1f21398
1 /*
2 * Samsung S3C24xx series LCD controller.
4 * Copyright (c) 2007 OpenMoko, Inc.
5 * Author: Andrzej Zaborowski <andrew@openedhand.com>
6 * With: Michel Pollet <buserror@gmail.com>
8 * This code is licenced under the GNU GPL v2.
9 */
11 #include "s3c.h"
12 #include "hw.h"
13 #include "console.h"
14 #include "framebuffer.h"
15 #include "sysemu.h"
18 struct s3c_lcd_state_s {
19 target_phys_addr_t base;
20 void *irq;
21 DisplayState *ds;
22 drawfn *line_fn;
24 uint32_t con[5];
25 uint32_t saddr[3];
26 uint32_t r;
27 uint32_t g;
28 uint16_t b;
29 uint32_t dithmode;
30 uint32_t tpal;
31 uint8_t intpnd;
32 uint8_t srcpnd;
33 uint8_t intmsk;
34 uint8_t lpcsel;
36 uint16_t raw_pal[0x100];
38 int width;
39 int height;
40 int bpp;
41 int enable;
42 int msb;
43 int frm565;
44 uint32_t palette[0x100];
45 int invalidate;
46 int invalidatep;
47 int src_width;
48 int dest_width;
49 drawfn fn;
52 static void s3c_lcd_update(struct s3c_lcd_state_s *s)
54 s->intpnd |= s->srcpnd & ~s->intmsk;
55 qemu_set_irq(s->irq, !!s->intpnd);
58 void s3c_lcd_reset(struct s3c_lcd_state_s *s)
60 s->enable = 0;
61 s->invalidate = 1;
62 s->invalidatep = 1;
63 s->width = -1;
64 s->height = -1;
66 s->con[0] = 0x00000000;
67 s->con[1] = 0x00000000;
68 s->con[2] = 0x00000000;
69 s->con[3] = 0x00000000;
70 s->con[4] = 0x00000000;
71 s->saddr[0] = 0x00000000;
72 s->saddr[1] = 0x00000000;
73 s->saddr[2] = 0x00000000;
74 s->r = 0x00000000;
75 s->g = 0x00000000;
76 s->b = 0x0000;
77 s->dithmode = 0x00000;
78 s->tpal = 0x00000000;
79 s->intpnd = 0;
80 s->srcpnd = 0;
81 s->intmsk = 3;
82 s->lpcsel = 4;
83 s3c_lcd_update(s);
86 #define S3C_LCDCON1 0x00 /* LCD Control register 1 */
87 #define S3C_LCDCON2 0x04 /* LCD Control register 2 */
88 #define S3C_LCDCON3 0x08 /* LCD Control register 3 */
89 #define S3C_LCDCON4 0x0c /* LCD Control register 4 */
90 #define S3C_LCDCON5 0x10 /* LCD Control register 5 */
91 #define S3C_LCDSADDR1 0x14 /* Framebuffer Start Address 1 register */
92 #define S3C_LCDSADDR2 0x18 /* Framebuffer Start Address 2 register */
93 #define S3C_LCDSADDR3 0x1c /* Framebuffer Start Address 3 register */
94 #define S3C_REDLUT 0x20 /* Red Lookup Table register */
95 #define S3C_GREENLUT 0x24 /* Green Lookup Table register */
96 #define S3C_BLUELUT 0x28 /* Blue Lookup Table register */
97 #define S3C_DITHMODE 0x4c /* Dithering Mode register */
98 #define S3C_TPAL 0x50 /* Temporary Palette register */
99 #define S3C_LCDINTPND 0x54 /* LCD Interrupt Pending register */
100 #define S3C_LCDSRCPND 0x58 /* LCD Interrupt Source Pending register */
101 #define S3C_LCDINTMSK 0x5c /* LCD Interrupt Mask register */
102 #define S3C_LPCSEL 0x60 /* LPC3600 Control register */
104 #define S3C_PALETTE 0x400 /* Palette IO start offset */
105 #define S3C_PALETTEEND 0x5ff /* Palette IO end offset */
107 static uint32_t s3c_lcd_read(void *opaque, target_phys_addr_t addr)
109 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
111 switch (addr) {
112 case S3C_LCDCON1:
113 return s->con[0]; /* XXX Return random LINECNT? */
114 case S3C_LCDCON2:
115 return s->con[1];
116 case S3C_LCDCON3:
117 return s->con[2];
118 case S3C_LCDCON4:
119 return s->con[3];
120 case S3C_LCDCON5:
121 return s->con[4]; /* XXX Return random STATUS? */
122 case S3C_LCDSADDR1:
123 return s->saddr[0];
124 case S3C_LCDSADDR2:
125 return s->saddr[1];
126 case S3C_LCDSADDR3:
127 return s->saddr[2];
128 case S3C_REDLUT:
129 return s->r;
130 case S3C_GREENLUT:
131 return s->g;
132 case S3C_BLUELUT:
133 return s->b;
134 case S3C_DITHMODE:
135 return s->dithmode;
136 case S3C_TPAL:
137 return s->tpal;
138 case S3C_LCDINTPND:
139 return s->intpnd;
140 case S3C_LCDSRCPND:
141 return s->srcpnd;
142 case S3C_LCDINTMSK:
143 return s->intmsk;
144 case S3C_LPCSEL:
145 return s->lpcsel;
146 case S3C_PALETTE ... S3C_PALETTEEND:
147 /* XXX assuming 16bit access */
148 return s->raw_pal[(addr - S3C_PALETTE) >> 1];
149 default:
150 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
151 break;
153 return 0;
156 static void s3c_lcd_write(void *opaque, target_phys_addr_t addr,
157 uint32_t value)
159 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
161 switch (addr) {
162 case S3C_LCDCON1:
163 s->con[0] = value & 0x0003ffff;
164 s->enable = value & 1;
165 s->bpp = (value >> 1) & 0xf;
166 s->invalidate = 1;
167 s->invalidatep = 1;
168 break;
169 case S3C_LCDCON2:
170 s->con[1] = value;
171 s->invalidate = 1;
172 break;
173 case S3C_LCDCON3:
174 s->con[2] = value;
175 s->invalidate = 1;
176 break;
177 case S3C_LCDCON4:
178 s->con[3] = value & 0xffff;
179 break;
180 case S3C_LCDCON5:
181 s->con[4] = value & 0x1fff;
182 s->frm565 = (value >> 11) & 1;
183 s->msb = (value >> 12) & 1;
184 s->invalidatep = 1;
185 s->invalidate = 1;
186 break;
187 case S3C_LCDSADDR1:
188 s->saddr[0] = value;
189 s->invalidate = 1;
190 break;
191 case S3C_LCDSADDR2:
192 s->saddr[1] = value;
193 s->invalidate = 1;
194 break;
195 case S3C_LCDSADDR3:
196 s->saddr[2] = value;
197 s->invalidate = 1;
198 break;
199 case S3C_REDLUT:
200 s->r = value;
201 s->invalidatep = 1;
202 s->invalidate = 1;
203 break;
204 case S3C_GREENLUT:
205 s->g = value;
206 s->invalidatep = 1;
207 s->invalidate = 1;
208 break;
209 case S3C_BLUELUT:
210 s->b = value;
211 s->invalidatep = 1;
212 s->invalidate = 1;
213 break;
214 case S3C_DITHMODE:
215 s->dithmode = value;
216 break;
217 case S3C_TPAL:
218 s->tpal = value;
219 s->invalidatep = 1;
220 s->invalidate = 1;
221 break;
222 case S3C_LCDINTPND:
223 s->intpnd = value & 3;
224 break;
225 case S3C_LCDSRCPND:
226 s->srcpnd = value & 3;
227 break;
228 case S3C_LCDINTMSK:
229 s->intmsk = value & 7;
230 s3c_lcd_update(s);
231 break;
232 case S3C_LPCSEL:
233 s->lpcsel = (value & 3) | 4;
234 if (value & 1)
235 printf("%s: attempt to enable LPC3600\n", __FUNCTION__);
236 break;
237 case S3C_PALETTE ... S3C_PALETTEEND:
238 /* XXX assuming 16bit access */
239 s->raw_pal[(addr - S3C_PALETTE) >> 1] = value;
240 break;
241 default:
242 printf("%s: Bad register 0x%lx\n", __FUNCTION__, (unsigned long)addr);
246 static CPUReadMemoryFunc *s3c_lcd_readfn[] = {
247 s3c_lcd_read,
248 s3c_lcd_read,
249 s3c_lcd_read,
252 static CPUWriteMemoryFunc *s3c_lcd_writefn[] = {
253 s3c_lcd_write,
254 s3c_lcd_write,
255 s3c_lcd_write,
258 static inline void s3c_lcd_resize(struct s3c_lcd_state_s *s)
260 int new_width, new_height;
261 new_height = ((s->con[1] >> 14) & 0x3ff) + 1;
262 new_width = ((s->con[2] >> 8) & 0x7ff) + 1;
263 if (s->width != new_width || s->height != new_height) {
264 s->width = new_width;
265 s->height = new_height;
267 if (graphic_rotate) {
268 qemu_console_resize(s->ds, s->height, s->width);
269 } else {
270 qemu_console_resize(s->ds, s->width, s->height);
272 s->invalidate = 1;
276 static inline
277 uint32_t s3c_rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
279 return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
282 static inline
283 uint32_t s3c_rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
285 return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
288 static inline
289 uint32_t s3c_rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
291 return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
294 static inline
295 uint32_t s3c_rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
297 return (r << 16) | (g << 8) | b;
300 static inline
301 uint32_t s3c_rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
303 return (r << 16) | (g << 8) | b;
306 static inline uint32_t s3c_rgb(struct s3c_lcd_state_s *s,
307 unsigned int r, unsigned int g, unsigned b)
309 switch (ds_get_bits_per_pixel(s->ds)) {
310 case 8:
311 return s3c_rgb_to_pixel32(r << 2, g << 2, b << 2);
312 case 15:
313 return s3c_rgb_to_pixel15(r << 2, g << 2, b << 2);
314 case 16:
315 return s3c_rgb_to_pixel16(r << 2, g << 2, b << 2);
316 case 24:
317 return s3c_rgb_to_pixel24(r << 2, g << 2, b << 2);
318 case 32:
319 return s3c_rgb_to_pixel32(r << 2, g << 2, b << 2);
320 default:
321 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
322 exit(1);
326 static void s3c_lcd_palette_load(struct s3c_lcd_state_s *s)
328 int i, n;
329 switch (s->bpp) {
330 case 0:
331 case 8:
332 n = 2;
333 s->src_width = s->width >> 3;
334 s->fn = s->line_fn[0];
335 break;
336 case 1:
337 case 9:
338 n = 4;
339 s->src_width = s->width >> 2;
340 s->fn = s->line_fn[1];
341 break;
342 case 2:
343 case 10:
344 n = 16;
345 s->src_width = s->width >> 1;
346 s->fn = s->line_fn[2];
347 break;
348 case 3:
349 case 11:
350 n = 256;
351 s->src_width = s->width >> 0;
352 s->fn = s->line_fn[3];
353 break;
354 case 6:
355 s->src_width = (s->width * 3) >> 1;
356 s->fn = s->line_fn[4];
357 return;
358 case 12:
359 s->src_width = s->width << 1;
360 if (s->frm565)
361 s->fn = s->line_fn[5];
362 else
363 s->fn = s->line_fn[6];
364 return;
365 case 13:
366 s->src_width = s->width << 2;
367 s->fn = s->line_fn[7];
368 return;
369 default:
370 return;
372 if (s->bpp & 8) {
373 for (i = 0; i < n; i ++)
374 if (s->frm565)
375 s->palette[i] = s3c_rgb(s,
376 (s->raw_pal[i] >> 10) & 0x3e,
377 (s->raw_pal[i] >> 5) & 0x3f,
378 (s->raw_pal[i] << 1) & 0x3e);
379 else
380 s->palette[i] = s3c_rgb(s,
381 ((s->raw_pal[i] >> 10) & 0x3e) | (s->raw_pal[i] & 1),
382 ((s->raw_pal[i] >> 6) & 0x3e) | (s->raw_pal[i] & 1),
383 s->raw_pal[i] & 0x3f);
384 } else {
385 for (i = 0; i < n; i ++)
386 if (n < 256)
387 s->palette[i] = s3c_rgb(s,
388 ((s->r >> (i * 4)) & 0xf) << 2,
389 ((s->g >> (i * 4)) & 0xf) << 2,
390 ((s->b >> (i * 4)) & 0xf) << 2);
391 else
392 s->palette[i] = s3c_rgb(s,
393 ((s->r >> (((i >> 5) & 7) * 4)) & 0xf) << 2,
394 ((s->g >> (((i >> 2) & 7) * 4)) & 0xf) << 2,
395 ((s->b >> ((i & 3) * 4)) & 0xf) << 2);
399 static void s3c_update_display(void *opaque)
401 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
402 int src_width;
403 ram_addr_t addr;
404 int first, last;
405 int dest_row_pitch, dest_col_pitch;
407 s3c_lcd_resize(s);
409 if (s->invalidatep) {
410 s3c_lcd_palette_load(s);
411 s->invalidatep = 0;
414 addr = s->saddr[0]<<1;
416 if (graphic_rotate) {
417 dest_row_pitch = s->dest_width;
418 dest_col_pitch = -s->height * s->dest_width;
419 } else {
420 dest_row_pitch = s->width * s->dest_width;
421 dest_col_pitch = s->dest_width;
423 src_width = s->src_width;
425 first = 0;
426 framebuffer_update_display(s->ds,
427 addr, s->width, s->height,
428 src_width, dest_row_pitch, dest_col_pitch,
429 s->invalidate,
430 s->fn, s->palette,
431 &first, &last);
433 s->srcpnd |= (1 << 1); /* INT_FrSyn */
434 s3c_lcd_update(s);
435 if (first >= 0) {
436 if (graphic_rotate)
437 dpy_update(s->ds, first, 0, last - first + 1, s->width);
438 else
439 dpy_update(s->ds, 0, first, s->width, last - first + 1);
441 s->invalidate = 0;
444 static void s3c_invalidate_display(void *opaque)
446 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
447 s->invalidate = 1;
450 static void s3c_screen_dump(void *opaque, const char *filename)
452 /* TODO */
455 #define BITS 8
456 #include "s3c24xx_template.h"
457 #define BITS 15
458 #include "s3c24xx_template.h"
459 #define BITS 16
460 #include "s3c24xx_template.h"
461 #define BITS 24
462 #include "s3c24xx_template.h"
463 #define BITS 32
464 #include "s3c24xx_template.h"
466 static void s3c_lcd_save(QEMUFile *f, void *opaque)
468 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
469 int i;
470 for (i = 0; i < 5; i ++)
471 qemu_put_be32s(f, &s->con[i]);
472 for (i = 0; i < 3; i ++)
473 qemu_put_be32s(f, &s->saddr[i]);
474 qemu_put_be32s(f, &s->r);
475 qemu_put_be32s(f, &s->g);
476 qemu_put_be16s(f, &s->b);
477 qemu_put_be32s(f, &s->dithmode);
478 qemu_put_be32s(f, &s->tpal);
479 qemu_put_8s(f, &s->intpnd);
480 qemu_put_8s(f, &s->srcpnd);
481 qemu_put_8s(f, &s->intmsk);
482 qemu_put_8s(f, &s->lpcsel);
483 for (i = 0; i < 0x100; i ++)
484 qemu_put_be16s(f, &s->raw_pal[i]);
487 static int s3c_lcd_load(QEMUFile *f, void *opaque, int version_id)
489 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *) opaque;
490 int i;
491 for (i = 0; i < 5; i ++)
492 qemu_get_be32s(f, &s->con[i]);
493 for (i = 0; i < 3; i ++)
494 qemu_get_be32s(f, &s->saddr[i]);
495 qemu_get_be32s(f, &s->r);
496 qemu_get_be32s(f, &s->g);
497 qemu_get_be16s(f, &s->b);
498 qemu_get_be32s(f, &s->dithmode);
499 qemu_get_be32s(f, &s->tpal);
500 qemu_get_8s(f, &s->intpnd);
501 qemu_get_8s(f, &s->srcpnd);
502 qemu_get_8s(f, &s->intmsk);
503 qemu_get_8s(f, &s->lpcsel);
505 s->invalidate = 1;
506 s->invalidatep = 1;
507 s->width = -1;
508 s->height = -1;
509 s->bpp = (s->con[0] >> 1) & 0xf;
510 s->enable = s->con[0] & 1;
511 s->msb = (s->con[4] >> 12) & 1;
512 s->frm565 = (s->con[4] >> 11) & 1;
514 for (i = 0; i < 0x100; i ++)
515 qemu_get_be16s(f, &s->raw_pal[i]);
517 return 0;
520 struct s3c_lcd_state_s *s3c_lcd_init(target_phys_addr_t base,
521 qemu_irq irq)
523 int iomemtype;
524 struct s3c_lcd_state_s *s = (struct s3c_lcd_state_s *)
525 qemu_mallocz(sizeof(struct s3c_lcd_state_s));
527 s->base = base;
528 s->irq = irq;
530 s3c_lcd_reset(s);
532 s->ds = graphic_console_init(
533 s3c_update_display,
534 s3c_invalidate_display,
535 s3c_screen_dump, NULL, s);
537 iomemtype = cpu_register_io_memory(0, s3c_lcd_readfn,
538 s3c_lcd_writefn, s);
539 cpu_register_physical_memory(s->base, 0xffffff, iomemtype);
541 register_savevm("s3c24xx_lcd", 0, 0, s3c_lcd_save, s3c_lcd_load, s);
543 switch (ds_get_bits_per_pixel(s->ds)) {
544 case 0:
545 s->dest_width = 0;
546 break;
547 case 8:
548 s->line_fn = s3c_draw_fn_8;
549 s->dest_width = 1;
550 break;
551 case 15:
552 s->line_fn = s3c_draw_fn_15;
553 s->dest_width = 2;
554 break;
555 case 16:
556 s->line_fn = s3c_draw_fn_16;
557 s->dest_width = 2;
558 break;
559 case 24:
560 s->line_fn = s3c_draw_fn_24;
561 s->dest_width = 3;
562 break;
563 case 32:
564 s->line_fn = s3c_draw_fn_32;
565 s->dest_width = 4;
566 break;
567 default:
568 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
569 exit(1);
571 return s;