Merge tag 'v9.0.0-rc3'
[qemu/ar7.git] / hw / arm / s3c24xx_lcd.c
blobdb5e560890337d7c862b8ac229fd24344bed2ef7
1 /*
2 * Samsung S3C24xx series LCD controller.
4 * Copyright (c) 2007 OpenMoko, Inc.
5 * Author: Andrzej Zaborowski <andrew@openedhand.com>
7 * Copyright 2010, 2013 Stefan Weil
9 * This code is licensed under the GNU GPL v2.
12 #include "qemu/osdep.h"
13 #include "cpu.h"
14 #include "hw/hw.h"
15 #include "ui/console.h"
16 #include "framebuffer.h"
17 #include "hw/sysbus.h"
19 #include "s3c24xx.h"
21 typedef struct {
22 SysBusDevice busdev;
23 MemoryRegion mmio;
24 qemu_irq irq;
25 //~ hwaddr base;
26 drawfn *line_fn;
27 QemuConsole *con;
28 uint32_t caddr[5];
29 uint32_t saddr[3];
30 uint32_t r;
31 uint32_t g;
32 uint16_t b;
33 uint32_t dithmode;
34 uint32_t tpal;
35 uint8_t intpnd;
36 uint8_t srcpnd;
37 uint8_t intmsk;
38 uint8_t lpcsel;
40 uint16_t raw_pal[0x100];
42 int width;
43 int height;
44 int bpp;
45 int enable;
46 int msb;
47 int frm565;
48 hwaddr fb;
49 uint32_t palette[0x100];
50 int invalidate;
51 int invalidatep;
52 int src_width;
53 int dest_width;
54 drawfn fn;
55 } S3C24xxLCD_State;
57 static void s3c24xx_lcd_update(S3C24xxLCD_State *s)
59 s->intpnd |= s->srcpnd & ~s->intmsk;
60 qemu_set_irq(s->irq, !!s->intpnd);
63 static void s3c24xx_lcd_reset(S3C24xxLCD_State *s)
65 s->enable = 0;
66 s->invalidate = 1;
67 s->invalidatep = 1;
68 s->width = -1;
69 s->height = -1;
71 s->caddr[0] = 0x00000000;
72 s->caddr[1] = 0x00000000;
73 s->caddr[2] = 0x00000000;
74 s->caddr[3] = 0x00000000;
75 s->caddr[4] = 0x00000000;
76 s->saddr[0] = 0x00000000;
77 s->saddr[1] = 0x00000000;
78 s->saddr[2] = 0x00000000;
79 s->r = 0x00000000;
80 s->g = 0x00000000;
81 s->b = 0x0000;
82 s->dithmode = 0x00000;
83 s->tpal = 0x00000000;
84 s->intpnd = 0;
85 s->srcpnd = 0;
86 s->intmsk = 3;
87 s->lpcsel = 4;
88 s3c24xx_lcd_update(s);
91 #define S3C24XX_LCDCON1 0x00 /* LCD Control register 1 */
92 #define S3C24XX_LCDCON2 0x04 /* LCD Control register 2 */
93 #define S3C24XX_LCDCON3 0x08 /* LCD Control register 3 */
94 #define S3C24XX_LCDCON4 0x0c /* LCD Control register 4 */
95 #define S3C24XX_LCDCON5 0x10 /* LCD Control register 5 */
96 #define S3C24XX_LCDSADDR1 0x14 /* Framebuffer Start Address 1 register */
97 #define S3C24XX_LCDSADDR2 0x18 /* Framebuffer Start Address 2 register */
98 #define S3C24XX_LCDSADDR3 0x1c /* Framebuffer Start Address 3 register */
99 #define S3C24XX_REDLUT 0x20 /* Red Lookup Table register */
100 #define S3C24XX_GREENLUT 0x24 /* Green Lookup Table register */
101 #define S3C24XX_BLUELUT 0x28 /* Blue Lookup Table register */
102 #define S3C24XX_DITHMODE 0x4c /* Dithering Mode register */
103 #define S3C24XX_TPAL 0x50 /* Temporary Palette register */
104 #define S3C24XX_LCDINTPND 0x54 /* LCD Interrupt Pending register */
105 #define S3C24XX_LCDSRCPND 0x58 /* LCD Interrupt Source Pending register */
106 #define S3C24XX_LCDINTMSK 0x5c /* LCD Interrupt Mask register */
107 #define S3C24XX_LPCSEL 0x60 /* LPC3600 Control register */
109 #define S3C24XX_PALETTE 0x400 /* Palette IO start offset */
110 #define S3C24XX_PALETTEEND 0x7fc /* Palette IO end offset */
112 static uint64_t s3c24xx_lcd_read(void *opaque,
113 hwaddr addr, unsigned size)
115 S3C24xxLCD_State *s = opaque;
117 switch (addr) {
118 case S3C24XX_LCDCON1:
119 return s->caddr[0]; /* XXX Return random LINECNT? */
120 case S3C24XX_LCDCON2:
121 return s->caddr[1];
122 case S3C24XX_LCDCON3:
123 return s->caddr[2];
124 case S3C24XX_LCDCON4:
125 return s->caddr[3];
126 case S3C24XX_LCDCON5:
127 return s->caddr[4]; /* XXX Return random STATUS? */
128 case S3C24XX_LCDSADDR1:
129 return s->saddr[0];
130 case S3C24XX_LCDSADDR2:
131 return s->saddr[1];
132 case S3C24XX_LCDSADDR3:
133 return s->saddr[2];
134 case S3C24XX_REDLUT:
135 return s->r;
136 case S3C24XX_GREENLUT:
137 return s->g;
138 case S3C24XX_BLUELUT:
139 return s->b;
140 case S3C24XX_DITHMODE:
141 return s->dithmode;
142 case S3C24XX_TPAL:
143 return s->tpal;
144 case S3C24XX_LCDINTPND:
145 return s->intpnd;
146 case S3C24XX_LCDSRCPND:
147 return s->srcpnd;
148 case S3C24XX_LCDINTMSK:
149 return s->intmsk;
150 case S3C24XX_LPCSEL:
151 return s->lpcsel;
152 case S3C24XX_PALETTE ... S3C24XX_PALETTEEND:
153 /* XXX assuming 16bit access */
154 return s->raw_pal[(addr - S3C24XX_PALETTE) >> 2];
155 default:
156 printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__, addr);
157 break;
159 return 0;
162 static void s3c24xx_lcd_write(void *opaque, hwaddr addr,
163 uint64_t value, unsigned size)
165 S3C24xxLCD_State *s = opaque;
167 switch (addr) {
168 case S3C24XX_LCDCON1:
169 s->caddr[0] = value & 0x0003ffff;
170 s->enable = value & 1;
171 s->bpp = (value >> 1) & 0xf;
172 s->invalidate = 1;
173 s->invalidatep = 1;
174 break;
175 case S3C24XX_LCDCON2:
176 s->caddr[1] = value;
177 s->invalidate = 1;
178 break;
179 case S3C24XX_LCDCON3:
180 s->caddr[2] = value;
181 s->invalidate = 1;
182 break;
183 case S3C24XX_LCDCON4:
184 s->caddr[3] = value & 0xffff;
185 break;
186 case S3C24XX_LCDCON5:
187 s->caddr[4] = value & 0x1fff;
188 s->frm565 = (value >> 11) & 1;
189 s->msb = (value >> 12) & 1;
190 s->invalidatep = 1;
191 s->invalidate = 1;
192 break;
193 case S3C24XX_LCDSADDR1:
194 s->saddr[0] = value;
195 s->fb = ((s->saddr[0] << 1) & 0x7ffffffe);
196 s->invalidate = 1;
197 break;
198 case S3C24XX_LCDSADDR2:
199 s->saddr[1] = value;
200 s->invalidate = 1;
201 break;
202 case S3C24XX_LCDSADDR3:
203 s->saddr[2] = value;
204 s->invalidate = 1;
205 break;
206 case S3C24XX_REDLUT:
207 s->r = value;
208 s->invalidatep = 1;
209 s->invalidate = 1;
210 break;
211 case S3C24XX_GREENLUT:
212 s->g = value;
213 s->invalidatep = 1;
214 s->invalidate = 1;
215 break;
216 case S3C24XX_BLUELUT:
217 s->b = value;
218 s->invalidatep = 1;
219 s->invalidate = 1;
220 break;
221 case S3C24XX_DITHMODE:
222 s->dithmode = value;
223 break;
224 case S3C24XX_TPAL:
225 s->tpal = value;
226 s->invalidatep = 1;
227 s->invalidate = 1;
228 break;
229 case S3C24XX_LCDINTPND:
230 s->intpnd = value & 3;
231 break;
232 case S3C24XX_LCDSRCPND:
233 s->srcpnd = value & 3;
234 break;
235 case S3C24XX_LCDINTMSK:
236 s->intmsk = value & 7;
237 s3c24xx_lcd_update(s);
238 break;
239 case S3C24XX_LPCSEL:
240 s->lpcsel = (value & 3) | 4;
241 if (value & 1)
242 printf("%s: attempt to enable LPC3600\n", __FUNCTION__);
243 break;
244 case S3C24XX_PALETTE ... S3C24XX_PALETTEEND:
245 /* XXX assuming 16bit access */
246 s->raw_pal[(addr - S3C24XX_PALETTE) >> 2] = value;
247 break;
248 default:
249 printf("%s: Bad register 0x" TARGET_FMT_plx "\n", __FUNCTION__, addr);
253 static const MemoryRegionOps s3c24xx_lcd_ops = {
254 .read = s3c24xx_lcd_read,
255 .write = s3c24xx_lcd_write,
256 .endianness = DEVICE_NATIVE_ENDIAN,
257 .valid = {
258 .min_access_size = 1,
259 .max_access_size = 4
263 static inline void s3c24xx_lcd_resize(S3C24xxLCD_State *s)
265 int new_width, new_height;
266 new_height = ((s->caddr[1] >> 14) & 0x3ff) + 1;
267 new_width = ((s->caddr[2] >> 8) & 0x7ff) + 1;
268 if (s->width != new_width || s->height != new_height) {
269 s->width = new_width;
270 s->height = new_height;
271 qemu_console_resize(s->con, s->width, s->height);
272 s->invalidate = 1;
276 static inline
277 uint32_t s3c24xx_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 s3c24xx_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 s3c24xx_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 s3c24xx_rgb_to_pixel24(unsigned int r, unsigned int g, unsigned b)
297 return (r << 16) | (g << 8) | b;
300 static inline
301 uint32_t s3c24xx_rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
303 return (r << 16) | (g << 8) | b;
306 static inline uint32_t s3c24xx_rgb(DisplaySurface *surface,
307 unsigned int r, unsigned int g, unsigned b)
309 switch (surface_bits_per_pixel(surface)) {
310 case 8:
311 return s3c24xx_rgb_to_pixel32(r << 2, g << 2, b << 2);
312 case 15:
313 return s3c24xx_rgb_to_pixel15(r << 2, g << 2, b << 2);
314 case 16:
315 return s3c24xx_rgb_to_pixel16(r << 2, g << 2, b << 2);
316 case 24:
317 return s3c24xx_rgb_to_pixel24(r << 2, g << 2, b << 2);
318 case 32:
319 return s3c24xx_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 s3c24xx_lcd_palette_load(S3C24xxLCD_State *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] = s3c24xx_rgb(qemu_console_surface(s->con),
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] = s3c24xx_rgb(qemu_console_surface(s->con),
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] = s3c24xx_rgb(qemu_console_surface(s->con),
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] = s3c24xx_rgb(qemu_console_surface(s->con),
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 s3c24xx_update_display(void *opaque)
401 S3C24xxLCD_State *s = opaque;
402 int src_width, dest_width, miny = 0, maxy = 0;
404 if (!s->enable || !s->dest_width)
405 return;
407 s3c24xx_lcd_resize(s);
409 if (s->invalidatep) {
410 s3c24xx_lcd_palette_load(s);
411 s->invalidatep = 0;
414 src_width = s->src_width;
415 dest_width = s->width * s->dest_width;
417 framebuffer_update_display(qemu_console_surface(s->con),
418 &s->mmio, s->width, s->height,
419 src_width, dest_width, s->dest_width,
420 0, s->invalidate,
421 s->fn, s->palette, &miny, &maxy);
424 s->srcpnd |= (1 << 1); /* INT_FrSyn */
425 s3c24xx_lcd_update(s);
426 dpy_gfx_update(s->con, 0, miny, s->width, maxy);
429 static void s3c24xx_invalidate_display(void *opaque)
431 S3C24xxLCD_State *s = opaque;
432 s->invalidate = 1;
435 static void s3c24xx_screen_dump(void *opaque, const char *filename,
436 bool cswitch, Error **errp)
438 /* TODO */
441 #define BITS 8
442 #include "s3c24xx_template.h"
443 #define BITS 15
444 #include "s3c24xx_template.h"
445 #define BITS 16
446 #include "s3c24xx_template.h"
447 #define BITS 24
448 #include "s3c24xx_template.h"
449 #define BITS 32
450 #include "s3c24xx_template.h"
452 #define S3C24XX_LCD_SIZE 0x1000000
454 static int s3c24xx_lcd_init(SysBusDevice *sbd)
456 DeviceState *dev = DEVICE(sbd);
457 S3C24xxLCD_State *s = S3C24XX_LCD(dev);
459 //~ s->brightness = 7;
461 memory_region_init_io(&s->mmio, &s3c24xx_lcd_ops, s,
462 "s3c24xx-lcd", S3C24XX_LCD_SIZE);
463 sysbus_init_mmio(sbd, &s->mmio);
465 sysbus_init_irq(dev, &s->irq);
467 s3c24xx_lcd_reset(s);
469 s->con = graphic_console_init(DEVICE(dev), &s3c24xx_gfx_ops, s);
470 //~ s3c24xx_update_display,
471 //~ s3c24xx_invalidate_display,
472 //~ s3c24xx_screen_dump, NULL, s);
474 //~ qdev_init_gpio_in(&dev->qdev, s3c24xx_lcd_gpio_brigthness_in, 3);
476 switch (surface_bits_per_pixel(qemu_console_surface(s->con))) {
477 case 0:
478 s->dest_width = 0;
479 break;
481 case 8:
482 s->line_fn = s3c24xx_draw_fn_8;
483 s->dest_width = 1;
484 break;
486 case 15:
487 s->line_fn = s3c24xx_draw_fn_15;
488 s->dest_width = 2;
489 break;
491 case 16:
492 s->line_fn = s3c24xx_draw_fn_16;
493 s->dest_width = 2;
494 break;
496 case 24:
497 s->line_fn = s3c24xx_draw_fn_24;
498 s->dest_width = 3;
499 break;
501 case 32:
502 s->line_fn = s3c24xx_draw_fn_32;
503 s->dest_width = 4;
504 break;
506 default:
507 fprintf(stderr, "%s: Bad color depth\n", __FUNCTION__);
508 exit(1);
511 return 0;
514 static const VMStateDescription s3c24xx_lcd_vmsd = {
515 .name = "s3c24xx_lcd",
516 .version_id = 1,
517 .minimum_version_id = 1,
518 .minimum_version_id_old = 1,
519 .fields = (VMStateField[]) {
520 //~ VMSTATE_UINT32(brightness, S3C24xxLCD_State),
521 //~ VMSTATE_UINT32(mode, S3C24xxLCD_State),
522 //~ VMSTATE_UINT32(irqctrl, S3C24xxLCD_State),
523 //~ VMSTATE_UINT32(page, S3C24xxLCD_State),
524 //~ VMSTATE_UINT32(page_off, S3C24xxLCD_State),
525 //~ VMSTATE_BUFFER(video_ram, S3C24xxLCD_State),
526 #if 0
527 for (i = 0; i < 5; i ++)
528 qemu_put_be32s(f, &s->con[i]);
529 for (i = 0; i < 3; i ++)
530 qemu_put_be32s(f, &s->saddr[i]);
531 qemu_put_be32s(f, &s->r);
532 qemu_put_be32s(f, &s->g);
533 qemu_put_be16s(f, &s->b);
534 qemu_put_be32s(f, &s->dithmode);
535 qemu_put_be32s(f, &s->tpal);
536 qemu_put_8s(f, &s->intpnd);
537 qemu_put_8s(f, &s->srcpnd);
538 qemu_put_8s(f, &s->intmsk);
539 qemu_put_8s(f, &s->lpcsel);
540 for (i = 0; i < 0x100; i ++)
541 qemu_put_be16s(f, &s->raw_pal[i]);
542 #elif 0
543 for (i = 0; i < 5; i ++)
544 qemu_get_be32s(f, &s->con[i]);
545 for (i = 0; i < 3; i ++)
546 qemu_get_be32s(f, &s->saddr[i]);
547 qemu_get_be32s(f, &s->r);
548 qemu_get_be32s(f, &s->g);
549 qemu_get_be16s(f, &s->b);
550 qemu_get_be32s(f, &s->dithmode);
551 qemu_get_be32s(f, &s->tpal);
552 qemu_get_8s(f, &s->intpnd);
553 qemu_get_8s(f, &s->srcpnd);
554 qemu_get_8s(f, &s->intmsk);
555 qemu_get_8s(f, &s->lpcsel);
557 s->invalidate = 1;
558 s->invalidatep = 1;
559 s->width = -1;
560 s->height = -1;
561 s->bpp = (s->con[0] >> 1) & 0xf;
562 s->enable = s->con[0] & 1;
563 s->msb = (s->con[4] >> 12) & 1;
564 s->frm565 = (s->con[4] >> 11) & 1;
565 s->fb = ((s->saddr[0] << 1) & 0x7ffffffe);
567 for (i = 0; i < 0x100; i ++)
568 qemu_get_be16s(f, &s->raw_pal[i]);
569 #endif
570 VMSTATE_END_OF_LIST()
574 static void s3c24xx_lcd_class_init(ObjectClass *klass, void *data)
576 DeviceClass *dc = DEVICE_CLASS(klass);
577 SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
578 dc->vmsd = &s3c24xx_lcd_vmsd;
579 k->init = s3c24xx_lcd_init;
582 static const TypeInfo s3c24xx_lcd_info = {
583 .name = "s3c24xx_lcd",
584 .parent = TYPE_SYS_BUS_DEVICE,
585 .instance_size = sizeof(S3C24xxLCD_State),
586 .class_init = s3c24xx_lcd_class_init
589 static void s3c24xx_lcd_register_types(void)
591 type_register_static(&s3c24xx_lcd_info);
594 type_init(s3c24xx_lcd_register_types)