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.
15 typedef void (*s3c_drawfn_t
)(uint32_t *, uint8_t *, const uint8_t *, int, int);
17 struct s3c_lcd_state_s
{
18 target_phys_addr_t base
;
21 s3c_drawfn_t
*line_fn
;
35 uint16_t raw_pal
[0x100];
44 uint32_t palette
[0x100];
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
)
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;
77 s
->dithmode
= 0x00000;
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
;
113 return s
->con
[0]; /* XXX Return random LINECNT? */
121 return s
->con
[4]; /* XXX Return random STATUS? */
146 case S3C_PALETTE
... S3C_PALETTEEND
:
147 /* XXX assuming 16bit access */
148 return s
->raw_pal
[(addr
- S3C_PALETTE
) >> 1];
150 printf("%s: Bad register 0x%lx\n", __FUNCTION__
, (unsigned long)addr
);
156 static void s3c_lcd_write(void *opaque
, target_phys_addr_t addr
,
159 struct s3c_lcd_state_s
*s
= (struct s3c_lcd_state_s
*) opaque
;
163 s
->con
[0] = value
& 0x0003ffff;
164 s
->enable
= value
& 1;
165 s
->bpp
= (value
>> 1) & 0xf;
178 s
->con
[3] = value
& 0xffff;
181 s
->con
[4] = value
& 0x1fff;
182 s
->frm565
= (value
>> 11) & 1;
183 s
->msb
= (value
>> 12) & 1;
189 /* s->fb = phys_ram_base +
190 (((s->saddr[0] << 1) & 0x7ffffffe) - S3C_RAM_BASE); */
191 s
->fb
= qemu_get_ram_ptr(
192 (((s
->saddr
[0] << 1) & 0x7ffffffe) - S3C_RAM_BASE
));
227 s
->intpnd
= value
& 3;
230 s
->srcpnd
= value
& 3;
233 s
->intmsk
= value
& 7;
237 s
->lpcsel
= (value
& 3) | 4;
239 printf("%s: attempt to enable LPC3600\n", __FUNCTION__
);
241 case S3C_PALETTE
... S3C_PALETTEEND
:
242 /* XXX assuming 16bit access */
243 s
->raw_pal
[(addr
- S3C_PALETTE
) >> 1] = value
;
246 printf("%s: Bad register 0x%lx\n", __FUNCTION__
, (unsigned long)addr
);
250 static CPUReadMemoryFunc
*s3c_lcd_readfn
[] = {
256 static CPUWriteMemoryFunc
*s3c_lcd_writefn
[] = {
262 static inline void s3c_lcd_resize(struct s3c_lcd_state_s
*s
)
264 int new_width
, new_height
;
265 new_height
= ((s
->con
[1] >> 14) & 0x3ff) + 1;
266 new_width
= ((s
->con
[2] >> 8) & 0x7ff) + 1;
267 if (s
->width
!= new_width
|| s
->height
!= new_height
) {
268 s
->width
= new_width
;
269 s
->height
= new_height
;
270 // dpy_resize(s->ds, s->width, s->height);
271 qemu_console_resize(s
->ds
, s
->width
, s
->height
);
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);
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);
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);
295 uint32_t s3c_rgb_to_pixel24(unsigned int r
, unsigned int g
, unsigned b
)
297 return (r
<< 16) | (g
<< 8) | b
;
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
)) {
311 return s3c_rgb_to_pixel32(r
<< 2, g
<< 2, b
<< 2);
313 return s3c_rgb_to_pixel15(r
<< 2, g
<< 2, b
<< 2);
315 return s3c_rgb_to_pixel16(r
<< 2, g
<< 2, b
<< 2);
317 return s3c_rgb_to_pixel24(r
<< 2, g
<< 2, b
<< 2);
319 return s3c_rgb_to_pixel32(r
<< 2, g
<< 2, b
<< 2);
321 fprintf(stderr
, "%s: Bad color depth\n", __FUNCTION__
);
326 static void s3c_lcd_palette_load(struct s3c_lcd_state_s
*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
] = s3c_rgb(s
,
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
] = 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);
385 for (i
= 0; i
< n
; i
++)
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);
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 y
, src_width
, dest_width
, dirty
[2], miny
, maxy
;
403 ram_addr_t x
, addr
, new_addr
, start
, end
;
405 if (!s
->enable
|| !s
->dest_width
)
410 if (s
->invalidatep
) {
411 s3c_lcd_palette_load(s
);
416 src_width
= s
->src_width
;
418 dest
= ds_get_data(s
->ds
);
419 dest_width
= s
->width
* s
->dest_width
;
421 /* addr = (ram_addr_t) (s->fb - (void *) phys_ram_base); */
422 addr
= qemu_ram_addr_from_host(s
->fb
);
423 start
= addr
+ s
->height
* src_width
;
425 dirty
[0] = dirty
[1] = cpu_physical_memory_get_dirty(start
, VGA_DIRTY_FLAG
);
428 for (y
= 0; y
< s
->height
; y
++) {
429 new_addr
= addr
+ src_width
;
430 for (x
= addr
+ TARGET_PAGE_SIZE
; x
< new_addr
;
431 x
+= TARGET_PAGE_SIZE
) {
432 dirty
[1] = cpu_physical_memory_get_dirty(x
, VGA_DIRTY_FLAG
);
433 dirty
[0] |= dirty
[1];
435 if (dirty
[0] || s
->invalidate
) {
436 s
->fn(s
->palette
, dest
, src
, s
->width
, s
->dest_width
);
452 cpu_physical_memory_reset_dirty(start
, end
, VGA_DIRTY_FLAG
);
453 s
->srcpnd
|= (1 << 1); /* INT_FrSyn */
455 dpy_update(s
->ds
, 0, miny
, s
->width
, maxy
);
458 static void s3c_invalidate_display(void *opaque
)
460 struct s3c_lcd_state_s
*s
= (struct s3c_lcd_state_s
*) opaque
;
464 static void s3c_screen_dump(void *opaque
, const char *filename
)
470 #include "s3c24xx_template.h"
472 #include "s3c24xx_template.h"
474 #include "s3c24xx_template.h"
476 #include "s3c24xx_template.h"
478 #include "s3c24xx_template.h"
480 static void s3c_lcd_save(QEMUFile
*f
, void *opaque
)
482 struct s3c_lcd_state_s
*s
= (struct s3c_lcd_state_s
*) opaque
;
484 for (i
= 0; i
< 5; i
++)
485 qemu_put_be32s(f
, &s
->con
[i
]);
486 for (i
= 0; i
< 3; i
++)
487 qemu_put_be32s(f
, &s
->saddr
[i
]);
488 qemu_put_be32s(f
, &s
->r
);
489 qemu_put_be32s(f
, &s
->g
);
490 qemu_put_be16s(f
, &s
->b
);
491 qemu_put_be32s(f
, &s
->dithmode
);
492 qemu_put_be32s(f
, &s
->tpal
);
493 qemu_put_8s(f
, &s
->intpnd
);
494 qemu_put_8s(f
, &s
->srcpnd
);
495 qemu_put_8s(f
, &s
->intmsk
);
496 qemu_put_8s(f
, &s
->lpcsel
);
497 for (i
= 0; i
< 0x100; i
++)
498 qemu_put_be16s(f
, &s
->raw_pal
[i
]);
501 static int s3c_lcd_load(QEMUFile
*f
, void *opaque
, int version_id
)
503 struct s3c_lcd_state_s
*s
= (struct s3c_lcd_state_s
*) opaque
;
505 for (i
= 0; i
< 5; i
++)
506 qemu_get_be32s(f
, &s
->con
[i
]);
507 for (i
= 0; i
< 3; i
++)
508 qemu_get_be32s(f
, &s
->saddr
[i
]);
509 qemu_get_be32s(f
, &s
->r
);
510 qemu_get_be32s(f
, &s
->g
);
511 qemu_get_be16s(f
, &s
->b
);
512 qemu_get_be32s(f
, &s
->dithmode
);
513 qemu_get_be32s(f
, &s
->tpal
);
514 qemu_get_8s(f
, &s
->intpnd
);
515 qemu_get_8s(f
, &s
->srcpnd
);
516 qemu_get_8s(f
, &s
->intmsk
);
517 qemu_get_8s(f
, &s
->lpcsel
);
523 s
->bpp
= (s
->con
[0] >> 1) & 0xf;
524 s
->enable
= s
->con
[0] & 1;
525 s
->msb
= (s
->con
[4] >> 12) & 1;
526 s
->frm565
= (s
->con
[4] >> 11) & 1;
527 /* s->fb = phys_ram_base + (((s->saddr[0] << 1) & 0x7ffffffe) - S3C_RAM_BASE); */
528 s
->fb
= qemu_get_ram_ptr((((s
->saddr
[0] << 1) & 0x7ffffffe) - S3C_RAM_BASE
));
530 for (i
= 0; i
< 0x100; i
++)
531 qemu_get_be16s(f
, &s
->raw_pal
[i
]);
536 struct s3c_lcd_state_s
*s3c_lcd_init(target_phys_addr_t base
,
540 struct s3c_lcd_state_s
*s
= (struct s3c_lcd_state_s
*)
541 qemu_mallocz(sizeof(struct s3c_lcd_state_s
));
548 s
->ds
= graphic_console_init(
550 s3c_invalidate_display
,
551 s3c_screen_dump
, NULL
, s
);
553 iomemtype
= cpu_register_io_memory(0, s3c_lcd_readfn
,
555 cpu_register_physical_memory(s
->base
, 0xffffff, iomemtype
);
557 register_savevm("s3c24xx_lcd", 0, 0, s3c_lcd_save
, s3c_lcd_load
, s
);
559 switch (ds_get_bits_per_pixel(s
->ds
)) {
564 s
->line_fn
= s3c_draw_fn_8
;
568 s
->line_fn
= s3c_draw_fn_15
;
572 s
->line_fn
= s3c_draw_fn_16
;
576 s
->line_fn
= s3c_draw_fn_24
;
580 s
->line_fn
= s3c_draw_fn_32
;
584 fprintf(stderr
, "%s: Bad color depth\n", __FUNCTION__
);