Commit 34f64942d902de45b380afb99de5faf704121029
1 parent
5eb9fc83
initial at91 lcdc implementation
Showing
4 changed files
with
231 additions
and
2 deletions
Makefile.target
| ... | ... | @@ -411,7 +411,7 @@ obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o |
| 411 | 411 | endif |
| 412 | 412 | |
| 413 | 413 | obj-arm-y = integratorcp.o versatilepb.o smc91c111.o arm_pic.o arm_timer.o |
| 414 | -obj-arm-y += pflash_atmel.o at91sam9.o spi_flash.o | |
| 414 | +obj-arm-y += pflash_atmel.o at91sam9.o spi_flash.o at91_lcdc.o | |
| 415 | 415 | obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o |
| 416 | 416 | obj-arm-y += versatile_pci.o |
| 417 | 417 | obj-arm-y += realview_gic.o realview.o arm_sysctl.o mpcore.o | ... | ... |
hw/at91_lcdc.c
0 → 100644
| 1 | +/* | |
| 2 | + * AT91 SAM9 LCD Controller (LCDC) | |
| 3 | + * Written by Evgeniy Dushistov | |
| 4 | + * This code is licenced under the GPL. | |
| 5 | + */ | |
| 6 | +#include "sysbus.h" | |
| 7 | +#include "console.h" | |
| 8 | +#include "pixel_ops.h" | |
| 9 | + | |
| 10 | +#define LCDC_SIZE 0x100000 | |
| 11 | + | |
| 12 | +#define LCDC_DMABADDR1 0x0 | |
| 13 | +#define LCDC_DMAFRMCFG 0x18 | |
| 14 | +#define LCDC_DMACON 0x1C | |
| 15 | +#define LCDC_LCDCON1 0x800 | |
| 16 | +#define LCDC_LCDCON2 0x804 | |
| 17 | +#define LCDC_LCDFRMCFG 0x810 | |
| 18 | +#define LCDC_PWRCON 0x83C | |
| 19 | + | |
| 20 | +#define DMACON_DMAEN 1 | |
| 21 | + | |
| 22 | +typedef struct LCDCState { | |
| 23 | + SysBusDevice busdev; | |
| 24 | + qemu_irq irq; | |
| 25 | + DisplayState *ds; | |
| 26 | + uint32_t dmacon; | |
| 27 | + uint32_t pwrcon; | |
| 28 | + uint32_t dmafrmcfg; | |
| 29 | + uint32_t lcdcon1; | |
| 30 | + uint32_t lcdcon2; | |
| 31 | + uint32_t lcdfrmcfg; | |
| 32 | + uint32_t dmabaddr1; | |
| 33 | +} LCDCState; | |
| 34 | + | |
| 35 | +#define AT91_LCDC_DEBUG | |
| 36 | +#ifdef AT91_LCDC_DEBUG | |
| 37 | +#define DPRINTF(fmt, ...) \ | |
| 38 | + do { \ | |
| 39 | + printf("AT91LCD: " fmt , ## __VA_ARGS__); \ | |
| 40 | + } while (0) | |
| 41 | +#else | |
| 42 | +#define DPRINTF(fmt, ...) do { } while (0) | |
| 43 | +#endif | |
| 44 | + | |
| 45 | + | |
| 46 | +static uint32_t at91_lcdc_mem_read(void *opaque, target_phys_addr_t offset) | |
| 47 | +{ | |
| 48 | + LCDCState *s = opaque; | |
| 49 | + | |
| 50 | + offset &= LCDC_SIZE - 1; | |
| 51 | + DPRINTF("read from %X\n", offset); | |
| 52 | + switch (offset) { | |
| 53 | + case LCDC_PWRCON: | |
| 54 | + return s->pwrcon; | |
| 55 | + case LCDC_DMACON: | |
| 56 | + return s->dmacon; | |
| 57 | + case LCDC_DMAFRMCFG: | |
| 58 | + return s->dmafrmcfg; | |
| 59 | + case LCDC_LCDCON1: | |
| 60 | + return s->lcdcon1; | |
| 61 | + case LCDC_LCDCON2: | |
| 62 | + return s->lcdcon2; | |
| 63 | + case LCDC_LCDFRMCFG: | |
| 64 | + return s->lcdfrmcfg; | |
| 65 | + default: | |
| 66 | + DPRINTF("unsup. read\n"); | |
| 67 | + return 0; | |
| 68 | + } | |
| 69 | +} | |
| 70 | + | |
| 71 | +static void at91_lcdc_mem_write(void *opaque, target_phys_addr_t offset, | |
| 72 | + uint32_t value) | |
| 73 | +{ | |
| 74 | + LCDCState *s = opaque; | |
| 75 | + offset &= LCDC_SIZE - 1; | |
| 76 | + DPRINTF("write to %X: %X\n", offset, value); | |
| 77 | + | |
| 78 | + switch (offset) { | |
| 79 | + case LCDC_DMABADDR1: | |
| 80 | + s->dmabaddr1 = value; | |
| 81 | + break; | |
| 82 | + case LCDC_PWRCON: | |
| 83 | + s->pwrcon = value; | |
| 84 | + break; | |
| 85 | + case LCDC_DMACON: | |
| 86 | + s->dmacon = value; | |
| 87 | + break; | |
| 88 | + case LCDC_DMAFRMCFG: | |
| 89 | + s->dmafrmcfg = value; | |
| 90 | + break; | |
| 91 | + case LCDC_LCDCON1: | |
| 92 | + s->lcdcon1 = value; | |
| 93 | + break; | |
| 94 | + case LCDC_LCDCON2: | |
| 95 | + DPRINTF("pixel size %u\n", (value >> 5) & 7); | |
| 96 | + s->lcdcon2 = value; | |
| 97 | + break; | |
| 98 | + case LCDC_LCDFRMCFG: | |
| 99 | + DPRINTF("lineval %u, linsize %u\n", value & 0x7FF, (value >> 21) & 0x7FF); | |
| 100 | + qemu_console_resize(s->ds, ((value >> 21) & 0x7FF) + 1, (value & 0x7FF) + 1); | |
| 101 | + s->lcdfrmcfg = value; | |
| 102 | + break; | |
| 103 | + default: | |
| 104 | + DPRINTF("unsup. write\n"); | |
| 105 | + } | |
| 106 | +} | |
| 107 | + | |
| 108 | +static CPUReadMemoryFunc *at91_lcdc_readfn[] = { | |
| 109 | + at91_lcdc_mem_read, | |
| 110 | + at91_lcdc_mem_read, | |
| 111 | + at91_lcdc_mem_read, | |
| 112 | +}; | |
| 113 | + | |
| 114 | +static CPUWriteMemoryFunc *at91_lcdc_writefn[] = { | |
| 115 | + at91_lcdc_mem_write, | |
| 116 | + at91_lcdc_mem_write, | |
| 117 | + at91_lcdc_mem_write, | |
| 118 | +}; | |
| 119 | + | |
| 120 | +static void at91_lcdc_reset(void *opaque) | |
| 121 | +{ | |
| 122 | + LCDCState *s = opaque; | |
| 123 | + s->pwrcon = 0x0000000e; | |
| 124 | + s->dmacon = 0; | |
| 125 | + s->dmafrmcfg = 0; | |
| 126 | + s->lcdcon1 = 0x00002000; | |
| 127 | + s->lcdfrmcfg = 0; | |
| 128 | +} | |
| 129 | + | |
| 130 | +struct pixel8 { | |
| 131 | + unsigned b : 2; | |
| 132 | + unsigned g : 3; | |
| 133 | + unsigned r : 3; | |
| 134 | +}; | |
| 135 | + | |
| 136 | +union pixel8u { | |
| 137 | + struct pixel8 p; | |
| 138 | + uint8_t val; | |
| 139 | +}; | |
| 140 | + | |
| 141 | +static void at91_lcdc_update_display(void *opaque) | |
| 142 | +{ | |
| 143 | + LCDCState *s = opaque; | |
| 144 | + uint32_t color; | |
| 145 | + int x, y; | |
| 146 | + uint8_t *d; | |
| 147 | + int width = ((s->lcdfrmcfg >> 21) & 0x7FF) + 1; | |
| 148 | + int height = (s->lcdfrmcfg & 0x7FF) + 1; | |
| 149 | + int q_bpp = (ds_get_bits_per_pixel(s->ds) + 7) >> 3; | |
| 150 | + int bpp; | |
| 151 | + int bpp_idx = (s->lcdcon2 >> 5) & 7; | |
| 152 | + union pixel8u tmp; | |
| 153 | + | |
| 154 | + DPRINTF("update begin\n"); | |
| 155 | + if (!(s->dmacon & DMACON_DMAEN)) | |
| 156 | + return; | |
| 157 | + DPRINTF("update continue\n"); | |
| 158 | + switch (bpp_idx) { | |
| 159 | + case 0 ... 4: | |
| 160 | + bpp = 1 << bpp_idx; | |
| 161 | + break; | |
| 162 | + case 5 ... 6: | |
| 163 | + bpp = 24; | |
| 164 | + break; | |
| 165 | + default: | |
| 166 | + fprintf(stderr, "Unknown pixel size\n"); | |
| 167 | + return;//reserved value, unknown pixel size | |
| 168 | + } | |
| 169 | + /*TODO: fix this restriction*/ | |
| 170 | + if (bpp != 8) { | |
| 171 | + fprintf(stderr, "Unsupported pixel size\n"); | |
| 172 | + return; | |
| 173 | + } | |
| 174 | + int once = 0; | |
| 175 | + for (y = 0; y < height; ++y) { | |
| 176 | + for (x = 0; x < width; ++x) { | |
| 177 | + cpu_physical_memory_read(s->dmabaddr1 + width * y + x, &tmp.val, 1); | |
| 178 | + if (tmp.val != 0 && once == 0) { | |
| 179 | + once = 1; | |
| 180 | + DPRINTF("not null %X, %X, %X, bpp %d\n", (unsigned)tmp.p.r << 5, (unsigned)tmp.p.g << 5, (unsigned)tmp.p.b << 6, ds_get_bits_per_pixel(s->ds)); | |
| 181 | + } | |
| 182 | + switch (ds_get_bits_per_pixel(s->ds)) { | |
| 183 | + case 8: | |
| 184 | + color = rgb_to_pixel8(tmp.p.r, tmp.p.g, tmp.p.b); | |
| 185 | + break; | |
| 186 | + case 15: | |
| 187 | + color = rgb_to_pixel15(tmp.p.r, tmp.p.g, tmp.p.b); | |
| 188 | + break; | |
| 189 | + case 16: | |
| 190 | + color = rgb_to_pixel16(tmp.p.r, tmp.p.g, tmp.p.b); | |
| 191 | + break; | |
| 192 | + case 24: | |
| 193 | + color = rgb_to_pixel24(tmp.p.r, tmp.p.g, tmp.p.b); | |
| 194 | + break; | |
| 195 | + case 32: | |
| 196 | + color = rgb_to_pixel32((unsigned)tmp.p.r << 5, (unsigned)tmp.p.g << 5, (unsigned)tmp.p.b << 6); | |
| 197 | + break; | |
| 198 | + default: | |
| 199 | + return; | |
| 200 | + } | |
| 201 | + | |
| 202 | + d = ds_get_data(s->ds) + ds_get_linesize(s->ds) * y + q_bpp * x; | |
| 203 | + *d = color; | |
| 204 | + } | |
| 205 | + } | |
| 206 | + | |
| 207 | + dpy_update(s->ds, 0, 0, width, height); | |
| 208 | +} | |
| 209 | + | |
| 210 | +static void at91_lcdc_init(SysBusDevice *dev) | |
| 211 | +{ | |
| 212 | + LCDCState *s = FROM_SYSBUS(typeof(*s), dev); | |
| 213 | + int lcdc_regs; | |
| 214 | + | |
| 215 | + sysbus_init_irq(dev, &s->irq); | |
| 216 | + lcdc_regs = cpu_register_io_memory(at91_lcdc_readfn, | |
| 217 | + at91_lcdc_writefn, s); | |
| 218 | + sysbus_init_mmio(dev, LCDC_SIZE, lcdc_regs); | |
| 219 | + qemu_register_reset(at91_lcdc_reset, s); | |
| 220 | + s->ds = graphic_console_init(at91_lcdc_update_display, NULL, NULL, NULL, s); | |
| 221 | +} | |
| 222 | + | |
| 223 | +static void at91_lcdc_register(void) | |
| 224 | +{ | |
| 225 | + sysbus_register_dev("at91,lcdc", sizeof(LCDCState), at91_lcdc_init); | |
| 226 | +} | |
| 227 | + | |
| 228 | +device_init(at91_lcdc_register) | ... | ... |
hw/at91sam9.c
| ... | ... | @@ -336,7 +336,7 @@ static void at91sam9_init(ram_addr_t ram_size, |
| 336 | 336 | sysbus_mmio_map(sysbus_from_qdev(dev), 0, AT91_EMAC_BASE); |
| 337 | 337 | sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[21]); |
| 338 | 338 | |
| 339 | - | |
| 339 | + sysbus_create_simple("at91,lcdc", AT91_LCDC_BASE, pic[26]); | |
| 340 | 340 | /* |
| 341 | 341 | we use variant of booting from external memory (NOR FLASH), |
| 342 | 342 | it mapped to 0x0 at start, and also it is accessable from 0x10000000 address | ... | ... |
hw/at91sam9263_defs.h