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"
15 #include "ui/console.h"
16 #include "framebuffer.h"
17 #include "hw/sysbus.h"
40 uint16_t raw_pal
[0x100];
49 uint32_t palette
[0x100];
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
)
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;
82 s
->dithmode
= 0x00000;
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
;
118 case S3C24XX_LCDCON1
:
119 return s
->caddr
[0]; /* XXX Return random LINECNT? */
120 case S3C24XX_LCDCON2
:
122 case S3C24XX_LCDCON3
:
124 case S3C24XX_LCDCON4
:
126 case S3C24XX_LCDCON5
:
127 return s
->caddr
[4]; /* XXX Return random STATUS? */
128 case S3C24XX_LCDSADDR1
:
130 case S3C24XX_LCDSADDR2
:
132 case S3C24XX_LCDSADDR3
:
136 case S3C24XX_GREENLUT
:
138 case S3C24XX_BLUELUT
:
140 case S3C24XX_DITHMODE
:
144 case S3C24XX_LCDINTPND
:
146 case S3C24XX_LCDSRCPND
:
148 case S3C24XX_LCDINTMSK
:
152 case S3C24XX_PALETTE
... S3C24XX_PALETTEEND
:
153 /* XXX assuming 16bit access */
154 return s
->raw_pal
[(addr
- S3C24XX_PALETTE
) >> 2];
156 printf("%s: Bad register 0x" TARGET_FMT_plx
"\n", __FUNCTION__
, addr
);
162 static void s3c24xx_lcd_write(void *opaque
, hwaddr addr
,
163 uint64_t value
, unsigned size
)
165 S3C24xxLCD_State
*s
= opaque
;
168 case S3C24XX_LCDCON1
:
169 s
->caddr
[0] = value
& 0x0003ffff;
170 s
->enable
= value
& 1;
171 s
->bpp
= (value
>> 1) & 0xf;
175 case S3C24XX_LCDCON2
:
179 case S3C24XX_LCDCON3
:
183 case S3C24XX_LCDCON4
:
184 s
->caddr
[3] = value
& 0xffff;
186 case S3C24XX_LCDCON5
:
187 s
->caddr
[4] = value
& 0x1fff;
188 s
->frm565
= (value
>> 11) & 1;
189 s
->msb
= (value
>> 12) & 1;
193 case S3C24XX_LCDSADDR1
:
195 s
->fb
= ((s
->saddr
[0] << 1) & 0x7ffffffe);
198 case S3C24XX_LCDSADDR2
:
202 case S3C24XX_LCDSADDR3
:
211 case S3C24XX_GREENLUT
:
216 case S3C24XX_BLUELUT
:
221 case S3C24XX_DITHMODE
:
229 case S3C24XX_LCDINTPND
:
230 s
->intpnd
= value
& 3;
232 case S3C24XX_LCDSRCPND
:
233 s
->srcpnd
= value
& 3;
235 case S3C24XX_LCDINTMSK
:
236 s
->intmsk
= value
& 7;
237 s3c24xx_lcd_update(s
);
240 s
->lpcsel
= (value
& 3) | 4;
242 printf("%s: attempt to enable LPC3600\n", __FUNCTION__
);
244 case S3C24XX_PALETTE
... S3C24XX_PALETTEEND
:
245 /* XXX assuming 16bit access */
246 s
->raw_pal
[(addr
- S3C24XX_PALETTE
) >> 2] = value
;
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
,
258 .min_access_size
= 1,
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
);
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);
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);
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);
295 uint32_t s3c24xx_rgb_to_pixel24(unsigned int r
, unsigned int g
, unsigned b
)
297 return (r
<< 16) | (g
<< 8) | b
;
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
)) {
311 return s3c24xx_rgb_to_pixel32(r
<< 2, g
<< 2, b
<< 2);
313 return s3c24xx_rgb_to_pixel15(r
<< 2, g
<< 2, b
<< 2);
315 return s3c24xx_rgb_to_pixel16(r
<< 2, g
<< 2, b
<< 2);
317 return s3c24xx_rgb_to_pixel24(r
<< 2, g
<< 2, b
<< 2);
319 return s3c24xx_rgb_to_pixel32(r
<< 2, g
<< 2, b
<< 2);
321 fprintf(stderr
, "%s: Bad color depth\n", __FUNCTION__
);
326 static void s3c24xx_lcd_palette_load(S3C24xxLCD_State
*s
)
333 s
->src_width
= s
->width
>> 3;
334 s
->fn
= s
->line_fn
[0];
339 s
->src_width
= s
->width
>> 2;
340 s
->fn
= s
->line_fn
[1];
345 s
->src_width
= s
->width
>> 1;
346 s
->fn
= s
->line_fn
[2];
351 s
->src_width
= s
->width
>> 0;
352 s
->fn
= s
->line_fn
[3];
355 s
->src_width
= (s
->width
* 3) >> 1;
356 s
->fn
= s
->line_fn
[4];
359 s
->src_width
= s
->width
<< 1;
361 s
->fn
= s
->line_fn
[5];
363 s
->fn
= s
->line_fn
[6];
366 s
->src_width
= s
->width
<< 2;
367 s
->fn
= s
->line_fn
[7];
373 for (i
= 0; i
< n
; i
++)
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);
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);
385 for (i
= 0; i
< n
; i
++)
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);
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
)
407 s3c24xx_lcd_resize(s
);
409 if (s
->invalidatep
) {
410 s3c24xx_lcd_palette_load(s
);
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
,
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
;
435 static void s3c24xx_screen_dump(void *opaque
, const char *filename
,
436 bool cswitch
, Error
**errp
)
442 #include "s3c24xx_template.h"
444 #include "s3c24xx_template.h"
446 #include "s3c24xx_template.h"
448 #include "s3c24xx_template.h"
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
))) {
482 s
->line_fn
= s3c24xx_draw_fn_8
;
487 s
->line_fn
= s3c24xx_draw_fn_15
;
492 s
->line_fn
= s3c24xx_draw_fn_16
;
497 s
->line_fn
= s3c24xx_draw_fn_24
;
502 s
->line_fn
= s3c24xx_draw_fn_32
;
507 fprintf(stderr
, "%s: Bad color depth\n", __FUNCTION__
);
514 static const VMStateDescription s3c24xx_lcd_vmsd
= {
515 .name
= "s3c24xx_lcd",
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),
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
]);
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
);
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
]);
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
)