diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c index fd0c8bc3d6..22c62dfebb 100644 --- a/hw/arm_sysctl.c +++ b/hw/arm_sysctl.c @@ -17,6 +17,8 @@ typedef struct { SysBusDevice busdev; + qemu_irq pl110_mux_ctrl; + uint32_t sys_id; uint32_t leds; uint16_t lockval; @@ -30,11 +32,12 @@ typedef struct { uint32_t sys_cfgdata; uint32_t sys_cfgctrl; uint32_t sys_cfgstat; + uint32_t sys_clcd; } arm_sysctl_state; static const VMStateDescription vmstate_arm_sysctl = { .name = "realview_sysctl", - .version_id = 2, + .version_id = 3, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(leds, arm_sysctl_state), @@ -48,6 +51,7 @@ static const VMStateDescription vmstate_arm_sysctl = { VMSTATE_UINT32_V(sys_cfgdata, arm_sysctl_state, 2), VMSTATE_UINT32_V(sys_cfgctrl, arm_sysctl_state, 2), VMSTATE_UINT32_V(sys_cfgstat, arm_sysctl_state, 2), + VMSTATE_UINT32_V(sys_clcd, arm_sysctl_state, 3), VMSTATE_END_OF_LIST() } }; @@ -78,6 +82,13 @@ static void arm_sysctl_reset(DeviceState *d) s->cfgdata2 = 0; s->flags = 0; s->resetlevel = 0; + if (board_id(s) == BOARD_ID_VEXPRESS) { + /* On VExpress this register will RAZ/WI */ + s->sys_clcd = 0; + } else { + /* All others: CLCDID 0x1f, indicating VGA */ + s->sys_clcd = 0x1f00; + } } static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset) @@ -124,7 +135,7 @@ static uint32_t arm_sysctl_read(void *opaque, target_phys_addr_t offset) case 0x4c: /* FLASH */ return 0; case 0x50: /* CLCD */ - return 0x1000; + return s->sys_clcd; case 0x54: /* CLCDSER */ return 0; case 0x58: /* BOOTCS */ @@ -232,7 +243,39 @@ static void arm_sysctl_write(void *opaque, target_phys_addr_t offset, /* nothing to do. */ break; case 0x4c: /* FLASH */ + break; case 0x50: /* CLCD */ + switch (board_id(s)) { + case BOARD_ID_PB926: + /* On 926 bits 13:8 are R/O, bits 1:0 control + * the mux that defines how to interpret the PL110 + * graphics format, and other bits are r/w but we + * don't implement them to do anything. + */ + s->sys_clcd &= 0x3f00; + s->sys_clcd |= val & ~0x3f00; + qemu_set_irq(s->pl110_mux_ctrl, val & 3); + break; + case BOARD_ID_EB: + /* The EB is the same except that there is no mux since + * the EB has a PL111. + */ + s->sys_clcd &= 0x3f00; + s->sys_clcd |= val & ~0x3f00; + break; + case BOARD_ID_PBA8: + case BOARD_ID_PBX: + /* On PBA8 and PBX bit 7 is r/w and all other bits + * are either r/o or RAZ/WI. + */ + s->sys_clcd &= (1 << 7); + s->sys_clcd |= val & ~(1 << 7); + break; + case BOARD_ID_VEXPRESS: + default: + /* On VExpress this register is unimplemented and will RAZ/WI */ + break; + } case 0x54: /* CLCDSER */ case 0x64: /* DMAPSR0 */ case 0x68: /* DMAPSR1 */ @@ -334,7 +377,7 @@ static int arm_sysctl_init1(SysBusDevice *dev) DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); qdev_init_gpio_in(&s->busdev.qdev, arm_sysctl_gpio_set, 2); - /* ??? Save/restore. */ + qdev_init_gpio_out(&s->busdev.qdev, &s->pl110_mux_ctrl, 1); return 0; } diff --git a/hw/pl110.c b/hw/pl110.c index 384eba2198..4ac710a6ec 100644 --- a/hw/pl110.c +++ b/hw/pl110.c @@ -53,6 +53,7 @@ typedef struct { int rows; enum pl110_bppmode bpp; int invalidate; + uint32_t mux_ctrl; uint32_t pallette[256]; uint32_t raw_pallette[128]; qemu_irq irq; @@ -60,7 +61,7 @@ typedef struct { static const VMStateDescription vmstate_pl110 = { .name = "pl110", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT32(version, pl110_state), @@ -76,6 +77,7 @@ static const VMStateDescription vmstate_pl110 = { VMSTATE_INT32(invalidate, pl110_state), VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256), VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128), + VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2), VMSTATE_END_OF_LIST() } }; @@ -173,16 +175,26 @@ static void pl110_update_display(void *opaque) * mux which allows bits to be reshuffled to give * 565 format. The mux is typically controlled by * an external system register. - * This should be controlled by a GPIO input pin + * This is controlled by a GPIO input pin * so boards can wire it up to their register. - * For now, force 16 bit to be 565, to match - * previous QEMU PL110 model behaviour. * * The PL111 straightforwardly implements both * 5551 and 565 under control of the bpp field * in the LCDControl register. */ - bpp_offset += (BPP_16_565 - BPP_16); + switch (s->mux_ctrl) { + case 3: /* 565 BGR */ + bpp_offset = (BPP_16_565 - BPP_16); + break; + case 1: /* 5551 */ + break; + case 0: /* 888; also if we have loaded vmstate from an old version */ + case 2: /* 565 RGB */ + default: + /* treat as 565 but honour BGR bit */ + bpp_offset += (BPP_16_565 - BPP_16); + break; + } } if (s->cr & PL110_CR_BEBO) @@ -416,6 +428,12 @@ static CPUWriteMemoryFunc * const pl110_writefn[] = { pl110_write }; +static void pl110_mux_ctrl_set(void *opaque, int line, int level) +{ + pl110_state *s = (pl110_state *)opaque; + s->mux_ctrl = level; +} + static int pl110_init(SysBusDevice *dev) { pl110_state *s = FROM_SYSBUS(pl110_state, dev); @@ -426,6 +444,7 @@ static int pl110_init(SysBusDevice *dev) DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); + qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1); s->ds = graphic_console_init(pl110_update_display, pl110_invalidate_display, NULL, NULL, s); diff --git a/hw/versatilepb.c b/hw/versatilepb.c index 147fe29b61..49f8f5fc56 100644 --- a/hw/versatilepb.c +++ b/hw/versatilepb.c @@ -180,7 +180,7 @@ static void versatile_init(ram_addr_t ram_size, qemu_irq *cpu_pic; qemu_irq pic[32]; qemu_irq sic[32]; - DeviceState *dev; + DeviceState *dev, *sysctl; PCIBus *pci_bus; NICInfo *nd; int n; @@ -198,7 +198,12 @@ static void versatile_init(ram_addr_t ram_size, /* SDRAM at address zero. */ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); - arm_sysctl_init(0x10000000, 0x41007004, 0x02000000); + sysctl = qdev_create(NULL, "realview_sysctl"); + qdev_prop_set_uint32(sysctl, "sys_id", 0x41007004); + qdev_init_nofail(sysctl); + qdev_prop_set_uint32(sysctl, "proc_id", 0x02000000); + sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000); + cpu_pic = arm_pic_init_cpu(env); dev = sysbus_create_varargs("pl190", 0x10140000, cpu_pic[0], cpu_pic[1], NULL); @@ -250,7 +255,9 @@ static void versatile_init(ram_addr_t ram_size, /* The versatile/PB actually has a modified Color LCD controller that includes hardware cursor support from the PL111. */ - sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]); + dev = sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]); + /* Wire up the mux control signals from the SYS_CLCD register */ + qdev_connect_gpio_out(sysctl, 0, qdev_get_gpio_in(dev, 0)); sysbus_create_varargs("pl181", 0x10005000, sic[22], sic[1], NULL); sysbus_create_varargs("pl181", 0x1000b000, sic[23], sic[2], NULL);