summary refs log tree commit diff stats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm_sysctl.c49
-rw-r--r--hw/pl110.c29
-rw-r--r--hw/versatilepb.c13
3 files changed, 80 insertions, 11 deletions
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);