aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-06-14 16:04:25 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-06-14 16:04:25 +0100
commit1be08a0946b1a189ac72822182c37367e8cd3d87 (patch)
tree2c41b2e3abf67957ea1b75e7d5330ab2b1dff4fe
parent7474f1be701f136b224af5e1abe55e97dc3f29a5 (diff)
parentfe8fcf3d642b4de1369841bf6acac13e0ec8770d (diff)
downloadqemu-1be08a0946b1a189ac72822182c37367e8cd3d87.zip
qemu-1be08a0946b1a189ac72822182c37367e8cd3d87.tar.gz
qemu-1be08a0946b1a189ac72822182c37367e8cd3d87.tar.bz2
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20160614-2' into staging
target-arm queue: * add PMU support for virt machine under KVM * fix reset and migration of TTBCR(S) * add virt-2.7 machine type * QOMify various ARM devices * implement xilinx DisplayPort device * don't permit ARMv8-only Neon insns to work on ARMv7 # gpg: Signature made Tue 14 Jun 2016 16:01:45 BST # gpg: using RSA key 0x3C2525ED14360CDE # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" # gpg: aka "Peter Maydell <pmaydell@gmail.com>" # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20160614-2: (30 commits) target-arm: Don't permit ARMv8-only Neon insns on ARMv7 arm: xlnx-zynqmp: Add xlnx-dp and xlnx-dpdma introduce xlnx-dp introduce xlnx-dpdma hw/i2c-ddc.c: Implement DDC I2C slave introduce dpcd module introduce aux-bus i2c: Factor our send() and recv() common logic i2c: implement broadcast write i2cbus: remove unused dev field hw/sd: QOM'ify pl181.c hw/dma: QOM'ify pxa2xx_dma.c hw/misc: QOM'ify mst_fpga.c hw/misc: QOM'ify exynos4210_pmu.c hw/misc: QOM'ify arm_l2x0.c hw/gpio: QOM'ify zaurus.c hw/gpio: QOM'ify pl061.c hw/gpio: QOM'ify omap_gpio.c hw/i2c: QOM'ify versatile_i2c.c hw/i2c: QOM'ify omap_i2c.c ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--default-configs/aarch64-softmmu.mak3
-rw-r--r--hw/arm/virt-acpi-build.c4
-rw-r--r--hw/arm/virt.c99
-rw-r--r--hw/arm/xlnx-zynqmp.c32
-rw-r--r--hw/display/Makefile.objs2
-rw-r--r--hw/display/dpcd.c173
-rw-r--r--hw/display/xlnx_dp.c1336
-rw-r--r--hw/dma/Makefile.objs1
-rw-r--r--hw/dma/pxa2xx_dma.c38
-rw-r--r--hw/dma/xlnx_dpdma.c786
-rw-r--r--hw/gpio/omap_gpio.c61
-rw-r--r--hw/gpio/pl061.c24
-rw-r--r--hw/gpio/zaurus.c14
-rw-r--r--hw/i2c/Makefile.objs1
-rw-r--r--hw/i2c/bitbang_i2c.c14
-rw-r--r--hw/i2c/core.c161
-rw-r--r--hw/i2c/exynos4210_i2c.c13
-rw-r--r--hw/i2c/i2c-ddc.c308
-rw-r--r--hw/i2c/omap_i2c.c42
-rw-r--r--hw/i2c/versatile_i2c.c19
-rw-r--r--hw/misc/Makefile.objs1
-rw-r--r--hw/misc/arm_l2x0.c11
-rw-r--r--hw/misc/aux.c292
-rw-r--r--hw/misc/exynos4210_pmu.c11
-rw-r--r--hw/misc/mst_fpga.c13
-rw-r--r--hw/sd/pl181.c26
-rw-r--r--include/hw/arm/virt.h4
-rw-r--r--include/hw/arm/xlnx-zynqmp.h4
-rw-r--r--include/hw/display/dpcd.h105
-rw-r--r--include/hw/display/xlnx_dp.h109
-rw-r--r--include/hw/dma/xlnx_dpdma.h85
-rw-r--r--include/hw/i2c/i2c-ddc.h38
-rw-r--r--include/hw/i2c/i2c.h1
-rw-r--r--include/hw/misc/aux.h128
-rw-r--r--target-arm/cpu.h2
-rw-r--r--target-arm/helper.c5
-rw-r--r--target-arm/kvm32.c6
-rw-r--r--target-arm/kvm64.c46
-rw-r--r--target-arm/kvm_arm.h7
-rw-r--r--target-arm/translate.c28
40 files changed, 3830 insertions, 223 deletions
diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak
index 96dd994..2449483 100644
--- a/default-configs/aarch64-softmmu.mak
+++ b/default-configs/aarch64-softmmu.mak
@@ -3,4 +3,7 @@
# We support all the 32 bit boards so need all their config
include arm-softmmu.mak
+CONFIG_AUX=y
+CONFIG_DDC=y
+CONFIG_DPCD=y
CONFIG_XLNX_ZYNQMP=y
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 735ab86..1fa0581 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -538,6 +538,10 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtGuestInfo *guest_info)
gicc->arm_mpidr = armcpu->mp_affinity;
gicc->uid = i;
gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
+
+ if (armcpu->has_pmu) {
+ gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ));
+ }
}
if (guest_info->gic_version == 3) {
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 73113cf..c5c125e 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -42,6 +42,7 @@
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "hw/boards.h"
+#include "hw/compat.h"
#include "hw/loader.h"
#include "exec/address-spaces.h"
#include "qemu/bitops.h"
@@ -98,6 +99,36 @@ typedef struct {
#define VIRT_MACHINE_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtMachineClass, klass, TYPE_VIRT_MACHINE)
+
+#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
+ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
+ void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ virt_machine_##major##_##minor##_options(mc); \
+ mc->desc = "QEMU " # major "." # minor " ARM Virtual Machine"; \
+ if (latest) { \
+ mc->alias = "virt"; \
+ } \
+ } \
+ static const TypeInfo machvirt_##major##_##minor##_info = { \
+ .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \
+ .parent = TYPE_VIRT_MACHINE, \
+ .instance_init = virt_##major##_##minor##_instance_init, \
+ .class_init = virt_##major##_##minor##_class_init, \
+ }; \
+ static void machvirt_machine_##major##_##minor##_init(void) \
+ { \
+ type_register_static(&machvirt_##major##_##minor##_info); \
+ } \
+ type_init(machvirt_machine_##major##_##minor##_init);
+
+#define DEFINE_VIRT_MACHINE_AS_LATEST(major, minor) \
+ DEFINE_VIRT_MACHINE_LATEST(major, minor, true)
+#define DEFINE_VIRT_MACHINE(major, minor) \
+ DEFINE_VIRT_MACHINE_LATEST(major, minor, false)
+
+
/* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means
* RAM can go up to the 256GB mark, leaving 256GB of the physical
* address space unallocated and free for future use between 256G and 512G.
@@ -436,6 +467,37 @@ static void fdt_add_gic_node(VirtBoardInfo *vbi, int type)
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle);
}
+static void fdt_add_pmu_nodes(const VirtBoardInfo *vbi, int gictype)
+{
+ CPUState *cpu;
+ ARMCPU *armcpu;
+ uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI;
+
+ CPU_FOREACH(cpu) {
+ armcpu = ARM_CPU(cpu);
+ if (!armcpu->has_pmu ||
+ !kvm_arm_pmu_create(cpu, PPI(VIRTUAL_PMU_IRQ))) {
+ return;
+ }
+ }
+
+ if (gictype == 2) {
+ irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
+ GIC_FDT_IRQ_PPI_CPU_WIDTH,
+ (1 << vbi->smp_cpus) - 1);
+ }
+
+ armcpu = ARM_CPU(qemu_get_cpu(0));
+ qemu_fdt_add_subnode(vbi->fdt, "/pmu");
+ if (arm_feature(&armcpu->env, ARM_FEATURE_V8)) {
+ const char compat[] = "arm,armv8-pmuv3";
+ qemu_fdt_setprop(vbi->fdt, "/pmu", "compatible",
+ compat, sizeof(compat));
+ qemu_fdt_setprop_cells(vbi->fdt, "/pmu", "interrupts",
+ GIC_FDT_IRQ_TYPE_PPI, VIRTUAL_PMU_IRQ, irqflags);
+ }
+}
+
static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
{
int i;
@@ -1259,6 +1321,8 @@ static void machvirt_init(MachineState *machine)
create_gic(vbi, pic, gic_version, vms->secure);
+ fdt_add_pmu_nodes(vbi, gic_version);
+
create_uart(vbi, pic, VIRT_UART, sysmem, serial_hds[0]);
if (vms->secure) {
@@ -1387,7 +1451,13 @@ static const TypeInfo virt_machine_info = {
.class_init = virt_machine_class_init,
};
-static void virt_2_6_instance_init(Object *obj)
+static void machvirt_machine_init(void)
+{
+ type_register_static(&virt_machine_info);
+}
+type_init(machvirt_machine_init);
+
+static void virt_2_7_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
@@ -1420,25 +1490,22 @@ static void virt_2_6_instance_init(Object *obj)
"Valid values are 2, 3 and host", NULL);
}
-static void virt_2_6_class_init(ObjectClass *oc, void *data)
+static void virt_machine_2_7_options(MachineClass *mc)
{
- MachineClass *mc = MACHINE_CLASS(oc);
-
- mc->desc = "QEMU 2.6 ARM Virtual Machine";
- mc->alias = "virt";
}
+DEFINE_VIRT_MACHINE_AS_LATEST(2, 7)
-static const TypeInfo machvirt_info = {
- .name = MACHINE_TYPE_NAME("virt-2.6"),
- .parent = TYPE_VIRT_MACHINE,
- .instance_init = virt_2_6_instance_init,
- .class_init = virt_2_6_class_init,
-};
+#define VIRT_COMPAT_2_6 \
+ HW_COMPAT_2_6
-static void machvirt_machine_init(void)
+static void virt_2_6_instance_init(Object *obj)
{
- type_register_static(&virt_machine_info);
- type_register_static(&machvirt_info);
+ virt_2_7_instance_init(obj);
}
-type_init(machvirt_machine_init);
+static void virt_machine_2_6_options(MachineClass *mc)
+{
+ virt_machine_2_7_options(mc);
+ SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_6);
+}
+DEFINE_VIRT_MACHINE(2, 6)
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 308d677..23c7199 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -38,6 +38,12 @@
#define SATA_ADDR 0xFD0C0000
#define SATA_NUM_PORTS 2
+#define DP_ADDR 0xfd4a0000
+#define DP_IRQ 113
+
+#define DPDMA_ADDR 0xfd4c0000
+#define DPDMA_IRQ 116
+
static const uint64_t gem_addr[XLNX_ZYNQMP_NUM_GEMS] = {
0xFF0B0000, 0xFF0C0000, 0xFF0D0000, 0xFF0E0000,
};
@@ -165,6 +171,12 @@ static void xlnx_zynqmp_init(Object *obj)
TYPE_XILINX_SPIPS);
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
}
+
+ object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP);
+ qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default());
+
+ object_initialize(&s->dpdma, sizeof(s->dpdma), TYPE_XLNX_DPDMA);
+ qdev_set_parent_bus(DEVICE(&s->dpdma), sysbus_get_default());
}
static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@@ -388,8 +400,26 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
object_property_add_alias(OBJECT(s), bus_name,
OBJECT(&s->spi[i]), "spi0",
&error_abort);
- g_free(bus_name);
+ g_free(bus_name);
+ }
+
+ object_property_set_bool(OBJECT(&s->dp), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->dp), 0, DP_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->dp), 0, gic_spi[DP_IRQ]);
+
+ object_property_set_bool(OBJECT(&s->dpdma), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
}
+ object_property_set_link(OBJECT(&s->dp), OBJECT(&s->dpdma), "dpdma",
+ &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->dpdma), 0, DPDMA_ADDR);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->dpdma), 0, gic_spi[DPDMA_IRQ]);
}
static Property xlnx_zynqmp_props[] = {
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index d99780e..063889b 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -43,3 +43,5 @@ virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
virtio-gpu.o-libs += $(VIRGL_LIBS)
virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS)
virtio-gpu-3d.o-libs += $(VIRGL_LIBS)
+obj-$(CONFIG_DPCD) += dpcd.o
+obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dp.o
diff --git a/hw/display/dpcd.c b/hw/display/dpcd.c
new file mode 100644
index 0000000..5a36855
--- /dev/null
+++ b/hw/display/dpcd.c
@@ -0,0 +1,173 @@
+/*
+ * dpcd.c
+ *
+ * Copyright (C) 2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This is a simple AUX slave which emulates a connected screen.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/misc/aux.h"
+#include "hw/display/dpcd.h"
+
+#ifndef DEBUG_DPCD
+#define DEBUG_DPCD 0
+#endif
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_DPCD) { \
+ qemu_log("dpcd: " fmt, ## __VA_ARGS__); \
+ } \
+} while (0);
+
+#define DPCD_READABLE_AREA 0x600
+
+struct DPCDState {
+ /*< private >*/
+ AUXSlave parent_obj;
+
+ /*< public >*/
+ /*
+ * The DCPD is 0x7FFFF length but read as 0 after offset 0x5FF.
+ */
+ uint8_t dpcd_info[DPCD_READABLE_AREA];
+
+ MemoryRegion iomem;
+};
+
+static uint64_t dpcd_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint8_t ret;
+ DPCDState *e = DPCD(opaque);
+
+ if (offset < DPCD_READABLE_AREA) {
+ ret = e->dpcd_info[offset];
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n",
+ offset);
+ ret = 0;
+ }
+
+ DPRINTF("read 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", ret, offset);
+ return ret;
+}
+
+static void dpcd_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ DPCDState *e = DPCD(opaque);
+
+ DPRINTF("write 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", (uint8_t)value, offset);
+
+ if (offset < DPCD_READABLE_AREA) {
+ e->dpcd_info[offset] = value;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n",
+ offset);
+ }
+}
+
+static const MemoryRegionOps aux_ops = {
+ .read = dpcd_read,
+ .write = dpcd_write,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+};
+
+static void dpcd_reset(DeviceState *dev)
+{
+ DPCDState *s = DPCD(dev);
+
+ memset(&(s->dpcd_info), 0, sizeof(s->dpcd_info));
+
+ s->dpcd_info[DPCD_REVISION] = DPCD_REV_1_0;
+ s->dpcd_info[DPCD_MAX_LINK_RATE] = DPCD_5_4GBPS;
+ s->dpcd_info[DPCD_MAX_LANE_COUNT] = DPCD_FOUR_LANES;
+ s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_0] = DPCD_EDID_PRESENT;
+ /* buffer size */
+ s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_1] = 0xFF;
+
+ s->dpcd_info[DPCD_LANE0_1_STATUS] = DPCD_LANE0_CR_DONE
+ | DPCD_LANE0_CHANNEL_EQ_DONE
+ | DPCD_LANE0_SYMBOL_LOCKED
+ | DPCD_LANE1_CR_DONE
+ | DPCD_LANE1_CHANNEL_EQ_DONE
+ | DPCD_LANE1_SYMBOL_LOCKED;
+ s->dpcd_info[DPCD_LANE2_3_STATUS] = DPCD_LANE2_CR_DONE
+ | DPCD_LANE2_CHANNEL_EQ_DONE
+ | DPCD_LANE2_SYMBOL_LOCKED
+ | DPCD_LANE3_CR_DONE
+ | DPCD_LANE3_CHANNEL_EQ_DONE
+ | DPCD_LANE3_SYMBOL_LOCKED;
+
+ s->dpcd_info[DPCD_LANE_ALIGN_STATUS_UPDATED] = DPCD_INTERLANE_ALIGN_DONE;
+ s->dpcd_info[DPCD_SINK_STATUS] = DPCD_RECEIVE_PORT_0_STATUS;
+}
+
+static void dpcd_init(Object *obj)
+{
+ DPCDState *s = DPCD(obj);
+
+ memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x7FFFF);
+ aux_init_mmio(AUX_SLAVE(obj), &s->iomem);
+}
+
+static const VMStateDescription vmstate_dpcd = {
+ .name = TYPE_DPCD,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void dpcd_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->reset = dpcd_reset;
+ dc->vmsd = &vmstate_dpcd;
+}
+
+static const TypeInfo dpcd_info = {
+ .name = TYPE_DPCD,
+ .parent = TYPE_AUX_SLAVE,
+ .instance_size = sizeof(DPCDState),
+ .class_init = dpcd_class_init,
+ .instance_init = dpcd_init,
+};
+
+static void dpcd_register_types(void)
+{
+ type_register_static(&dpcd_info);
+}
+
+type_init(dpcd_register_types)
diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c
new file mode 100644
index 0000000..be53b75
--- /dev/null
+++ b/hw/display/xlnx_dp.c
@@ -0,0 +1,1336 @@
+/*
+ * xlnx_dp.c
+ *
+ * Copyright (C) 2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/display/xlnx_dp.h"
+
+#ifndef DEBUG_DP
+#define DEBUG_DP 0
+#endif
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_DP) { \
+ qemu_log("xlnx_dp: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0);
+
+/*
+ * Register offset for DP.
+ */
+#define DP_LINK_BW_SET (0x0000 >> 2)
+#define DP_LANE_COUNT_SET (0x0004 >> 2)
+#define DP_ENHANCED_FRAME_EN (0x0008 >> 2)
+#define DP_TRAINING_PATTERN_SET (0x000C >> 2)
+#define DP_LINK_QUAL_PATTERN_SET (0x0010 >> 2)
+#define DP_SCRAMBLING_DISABLE (0x0014 >> 2)
+#define DP_DOWNSPREAD_CTRL (0x0018 >> 2)
+#define DP_SOFTWARE_RESET (0x001C >> 2)
+#define DP_TRANSMITTER_ENABLE (0x0080 >> 2)
+#define DP_MAIN_STREAM_ENABLE (0x0084 >> 2)
+#define DP_FORCE_SCRAMBLER_RESET (0x00C0 >> 2)
+#define DP_VERSION_REGISTER (0x00F8 >> 2)
+#define DP_CORE_ID (0x00FC >> 2)
+
+#define DP_AUX_COMMAND_REGISTER (0x0100 >> 2)
+#define AUX_ADDR_ONLY_MASK (0x1000)
+#define AUX_COMMAND_MASK (0x0F00)
+#define AUX_COMMAND_SHIFT (8)
+#define AUX_COMMAND_NBYTES (0x000F)
+
+#define DP_AUX_WRITE_FIFO (0x0104 >> 2)
+#define DP_AUX_ADDRESS (0x0108 >> 2)
+#define DP_AUX_CLOCK_DIVIDER (0x010C >> 2)
+#define DP_TX_USER_FIFO_OVERFLOW (0x0110 >> 2)
+#define DP_INTERRUPT_SIGNAL_STATE (0x0130 >> 2)
+#define DP_AUX_REPLY_DATA (0x0134 >> 2)
+#define DP_AUX_REPLY_CODE (0x0138 >> 2)
+#define DP_AUX_REPLY_COUNT (0x013C >> 2)
+#define DP_REPLY_DATA_COUNT (0x0148 >> 2)
+#define DP_REPLY_STATUS (0x014C >> 2)
+#define DP_HPD_DURATION (0x0150 >> 2)
+#define DP_MAIN_STREAM_HTOTAL (0x0180 >> 2)
+#define DP_MAIN_STREAM_VTOTAL (0x0184 >> 2)
+#define DP_MAIN_STREAM_POLARITY (0x0188 >> 2)
+#define DP_MAIN_STREAM_HSWIDTH (0x018C >> 2)
+#define DP_MAIN_STREAM_VSWIDTH (0x0190 >> 2)
+#define DP_MAIN_STREAM_HRES (0x0194 >> 2)
+#define DP_MAIN_STREAM_VRES (0x0198 >> 2)
+#define DP_MAIN_STREAM_HSTART (0x019C >> 2)
+#define DP_MAIN_STREAM_VSTART (0x01A0 >> 2)
+#define DP_MAIN_STREAM_MISC0 (0x01A4 >> 2)
+#define DP_MAIN_STREAM_MISC1 (0x01A8 >> 2)
+#define DP_MAIN_STREAM_M_VID (0x01AC >> 2)
+#define DP_MSA_TRANSFER_UNIT_SIZE (0x01B0 >> 2)
+#define DP_MAIN_STREAM_N_VID (0x01B4 >> 2)
+#define DP_USER_DATA_COUNT_PER_LANE (0x01BC >> 2)
+#define DP_MIN_BYTES_PER_TU (0x01C4 >> 2)
+#define DP_FRAC_BYTES_PER_TU (0x01C8 >> 2)
+#define DP_INIT_WAIT (0x01CC >> 2)
+#define DP_PHY_RESET (0x0200 >> 2)
+#define DP_PHY_VOLTAGE_DIFF_LANE_0 (0x0220 >> 2)
+#define DP_PHY_VOLTAGE_DIFF_LANE_1 (0x0224 >> 2)
+#define DP_TRANSMIT_PRBS7 (0x0230 >> 2)
+#define DP_PHY_CLOCK_SELECT (0x0234 >> 2)
+#define DP_TX_PHY_POWER_DOWN (0x0238 >> 2)
+#define DP_PHY_PRECURSOR_LANE_0 (0x023C >> 2)
+#define DP_PHY_PRECURSOR_LANE_1 (0x0240 >> 2)
+#define DP_PHY_POSTCURSOR_LANE_0 (0x024C >> 2)
+#define DP_PHY_POSTCURSOR_LANE_1 (0x0250 >> 2)
+#define DP_PHY_STATUS (0x0280 >> 2)
+
+#define DP_TX_AUDIO_CONTROL (0x0300 >> 2)
+#define DP_TX_AUD_CTRL (1)
+
+#define DP_TX_AUDIO_CHANNELS (0x0304 >> 2)
+#define DP_TX_AUDIO_INFO_DATA(n) ((0x0308 + 4 * n) >> 2)
+#define DP_TX_M_AUD (0x0328 >> 2)
+#define DP_TX_N_AUD (0x032C >> 2)
+#define DP_TX_AUDIO_EXT_DATA(n) ((0x0330 + 4 * n) >> 2)
+#define DP_INT_STATUS (0x03A0 >> 2)
+#define DP_INT_MASK (0x03A4 >> 2)
+#define DP_INT_EN (0x03A8 >> 2)
+#define DP_INT_DS (0x03AC >> 2)
+
+/*
+ * Registers offset for Audio Video Buffer configuration.
+ */
+#define V_BLEND_OFFSET (0xA000)
+#define V_BLEND_BG_CLR_0 (0x0000 >> 2)
+#define V_BLEND_BG_CLR_1 (0x0004 >> 2)
+#define V_BLEND_BG_CLR_2 (0x0008 >> 2)
+#define V_BLEND_SET_GLOBAL_ALPHA_REG (0x000C >> 2)
+#define V_BLEND_OUTPUT_VID_FORMAT (0x0014 >> 2)
+#define V_BLEND_LAYER0_CONTROL (0x0018 >> 2)
+#define V_BLEND_LAYER1_CONTROL (0x001C >> 2)
+
+#define V_BLEND_RGB2YCBCR_COEFF(n) ((0x0020 + 4 * n) >> 2)
+#define V_BLEND_IN1CSC_COEFF(n) ((0x0044 + 4 * n) >> 2)
+
+#define V_BLEND_LUMA_IN1CSC_OFFSET (0x0068 >> 2)
+#define V_BLEND_CR_IN1CSC_OFFSET (0x006C >> 2)
+#define V_BLEND_CB_IN1CSC_OFFSET (0x0070 >> 2)
+#define V_BLEND_LUMA_OUTCSC_OFFSET (0x0074 >> 2)
+#define V_BLEND_CR_OUTCSC_OFFSET (0x0078 >> 2)
+#define V_BLEND_CB_OUTCSC_OFFSET (0x007C >> 2)
+
+#define V_BLEND_IN2CSC_COEFF(n) ((0x0080 + 4 * n) >> 2)
+
+#define V_BLEND_LUMA_IN2CSC_OFFSET (0x00A4 >> 2)
+#define V_BLEND_CR_IN2CSC_OFFSET (0x00A8 >> 2)
+#define V_BLEND_CB_IN2CSC_OFFSET (0x00AC >> 2)
+#define V_BLEND_CHROMA_KEY_ENABLE (0x01D0 >> 2)
+#define V_BLEND_CHROMA_KEY_COMP1 (0x01D4 >> 2)
+#define V_BLEND_CHROMA_KEY_COMP2 (0x01D8 >> 2)
+#define V_BLEND_CHROMA_KEY_COMP3 (0x01DC >> 2)
+
+/*
+ * Registers offset for Audio Video Buffer configuration.
+ */
+#define AV_BUF_MANAGER_OFFSET (0xB000)
+#define AV_BUF_FORMAT (0x0000 >> 2)
+#define AV_BUF_NON_LIVE_LATENCY (0x0008 >> 2)
+#define AV_CHBUF0 (0x0010 >> 2)
+#define AV_CHBUF1 (0x0014 >> 2)
+#define AV_CHBUF2 (0x0018 >> 2)
+#define AV_CHBUF3 (0x001C >> 2)
+#define AV_CHBUF4 (0x0020 >> 2)
+#define AV_CHBUF5 (0x0024 >> 2)
+#define AV_BUF_STC_CONTROL (0x002C >> 2)
+#define AV_BUF_STC_INIT_VALUE0 (0x0030 >> 2)
+#define AV_BUF_STC_INIT_VALUE1 (0x0034 >> 2)
+#define AV_BUF_STC_ADJ (0x0038 >> 2)
+#define AV_BUF_STC_VIDEO_VSYNC_TS_REG0 (0x003C >> 2)
+#define AV_BUF_STC_VIDEO_VSYNC_TS_REG1 (0x0040 >> 2)
+#define AV_BUF_STC_EXT_VSYNC_TS_REG0 (0x0044 >> 2)
+#define AV_BUF_STC_EXT_VSYNC_TS_REG1 (0x0048 >> 2)
+#define AV_BUF_STC_CUSTOM_EVENT_TS_REG0 (0x004C >> 2)
+#define AV_BUF_STC_CUSTOM_EVENT_TS_REG1 (0x0050 >> 2)
+#define AV_BUF_STC_CUSTOM_EVENT2_TS_REG0 (0x0054 >> 2)
+#define AV_BUF_STC_CUSTOM_EVENT2_TS_REG1 (0x0058 >> 2)
+#define AV_BUF_STC_SNAPSHOT0 (0x0060 >> 2)
+#define AV_BUF_STC_SNAPSHOT1 (0x0064 >> 2)
+#define AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT (0x0070 >> 2)
+#define AV_BUF_HCOUNT_VCOUNT_INT0 (0x0074 >> 2)
+#define AV_BUF_HCOUNT_VCOUNT_INT1 (0x0078 >> 2)
+#define AV_BUF_DITHER_CONFIG (0x007C >> 2)
+#define AV_BUF_DITHER_CONFIG_MAX (0x008C >> 2)
+#define AV_BUF_DITHER_CONFIG_MIN (0x0090 >> 2)
+#define AV_BUF_PATTERN_GEN_SELECT (0x0100 >> 2)
+#define AV_BUF_AUD_VID_CLK_SOURCE (0x0120 >> 2)
+#define AV_BUF_SRST_REG (0x0124 >> 2)
+#define AV_BUF_AUDIO_RDY_INTERVAL (0x0128 >> 2)
+#define AV_BUF_AUDIO_CH_CONFIG (0x012C >> 2)
+
+#define AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(n)((0x0200 + 4 * n) >> 2)
+
+#define AV_BUF_VIDEO_COMP_SCALE_FACTOR(n) ((0x020C + 4 * n) >> 2)
+
+#define AV_BUF_LIVE_VIDEO_COMP_SF(n) ((0x0218 + 4 * n) >> 2)
+
+#define AV_BUF_LIVE_VID_CONFIG (0x0224 >> 2)
+
+#define AV_BUF_LIVE_GFX_COMP_SF(n) ((0x0228 + 4 * n) >> 2)
+
+#define AV_BUF_LIVE_GFX_CONFIG (0x0234 >> 2)
+
+#define AUDIO_MIXER_REGISTER_OFFSET (0xC000)
+#define AUDIO_MIXER_VOLUME_CONTROL (0x0000 >> 2)
+#define AUDIO_MIXER_META_DATA (0x0004 >> 2)
+#define AUD_CH_STATUS_REG(n) ((0x0008 + 4 * n) >> 2)
+#define AUD_CH_A_DATA_REG(n) ((0x0020 + 4 * n) >> 2)
+#define AUD_CH_B_DATA_REG(n) ((0x0038 + 4 * n) >> 2)
+
+#define DP_AUDIO_DMA_CHANNEL(n) (4 + n)
+#define DP_GRAPHIC_DMA_CHANNEL (3)
+#define DP_VIDEO_DMA_CHANNEL (0)
+
+enum DPGraphicFmt {
+ DP_GRAPHIC_RGBA8888 = 0 << 8,
+ DP_GRAPHIC_ABGR8888 = 1 << 8,
+ DP_GRAPHIC_RGB888 = 2 << 8,
+ DP_GRAPHIC_BGR888 = 3 << 8,
+ DP_GRAPHIC_RGBA5551 = 4 << 8,
+ DP_GRAPHIC_RGBA4444 = 5 << 8,
+ DP_GRAPHIC_RGB565 = 6 << 8,
+ DP_GRAPHIC_8BPP = 7 << 8,
+ DP_GRAPHIC_4BPP = 8 << 8,
+ DP_GRAPHIC_2BPP = 9 << 8,
+ DP_GRAPHIC_1BPP = 10 << 8,
+ DP_GRAPHIC_MASK = 0xF << 8
+};
+
+enum DPVideoFmt {
+ DP_NL_VID_CB_Y0_CR_Y1 = 0,
+ DP_NL_VID_CR_Y0_CB_Y1 = 1,
+ DP_NL_VID_Y0_CR_Y1_CB = 2,
+ DP_NL_VID_Y0_CB_Y1_CR = 3,
+ DP_NL_VID_YV16 = 4,
+ DP_NL_VID_YV24 = 5,
+ DP_NL_VID_YV16CL = 6,
+ DP_NL_VID_MONO = 7,
+ DP_NL_VID_YV16CL2 = 8,
+ DP_NL_VID_YUV444 = 9,
+ DP_NL_VID_RGB888 = 10,
+ DP_NL_VID_RGBA8880 = 11,
+ DP_NL_VID_RGB888_10BPC = 12,
+ DP_NL_VID_YUV444_10BPC = 13,
+ DP_NL_VID_YV16CL2_10BPC = 14,
+ DP_NL_VID_YV16CL_10BPC = 15,
+ DP_NL_VID_YV16_10BPC = 16,
+ DP_NL_VID_YV24_10BPC = 17,
+ DP_NL_VID_Y_ONLY_10BPC = 18,
+ DP_NL_VID_YV16_420 = 19,
+ DP_NL_VID_YV16CL_420 = 20,
+ DP_NL_VID_YV16CL2_420 = 21,
+ DP_NL_VID_YV16_420_10BPC = 22,
+ DP_NL_VID_YV16CL_420_10BPC = 23,
+ DP_NL_VID_YV16CL2_420_10BPC = 24,
+ DP_NL_VID_FMT_MASK = 0x1F
+};
+
+typedef enum DPGraphicFmt DPGraphicFmt;
+typedef enum DPVideoFmt DPVideoFmt;
+
+static const VMStateDescription vmstate_dp = {
+ .name = TYPE_XLNX_DP,
+ .version_id = 1,
+ .fields = (VMStateField[]){
+ VMSTATE_UINT32_ARRAY(core_registers, XlnxDPState,
+ DP_CORE_REG_ARRAY_SIZE),
+ VMSTATE_UINT32_ARRAY(avbufm_registers, XlnxDPState,
+ DP_AVBUF_REG_ARRAY_SIZE),
+ VMSTATE_UINT32_ARRAY(vblend_registers, XlnxDPState,
+ DP_VBLEND_REG_ARRAY_SIZE),
+ VMSTATE_UINT32_ARRAY(audio_registers, XlnxDPState,
+ DP_AUDIO_REG_ARRAY_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xlnx_dp_update_irq(XlnxDPState *s);
+
+static uint64_t xlnx_dp_audio_read(void *opaque, hwaddr offset, unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ offset = offset >> 2;
+ return s->audio_registers[offset];
+}
+
+static void xlnx_dp_audio_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ offset = offset >> 2;
+
+ switch (offset) {
+ case AUDIO_MIXER_META_DATA:
+ s->audio_registers[offset] = value & 0x00000001;
+ break;
+ default:
+ s->audio_registers[offset] = value;
+ break;
+ }
+}
+
+static const MemoryRegionOps audio_ops = {
+ .read = xlnx_dp_audio_read,
+ .write = xlnx_dp_audio_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static inline uint32_t xlnx_dp_audio_get_volume(XlnxDPState *s,
+ uint8_t channel)
+{
+ switch (channel) {
+ case 0:
+ return extract32(s->audio_registers[AUDIO_MIXER_VOLUME_CONTROL], 0, 16);
+ case 1:
+ return extract32(s->audio_registers[AUDIO_MIXER_VOLUME_CONTROL], 16,
+ 16);
+ default:
+ return 0;
+ }
+}
+
+static inline void xlnx_dp_audio_activate(XlnxDPState *s)
+{
+ bool activated = ((s->core_registers[DP_TX_AUDIO_CONTROL]
+ & DP_TX_AUD_CTRL) != 0);
+ AUD_set_active_out(s->amixer_output_stream, activated);
+ xlnx_dpdma_set_host_data_location(s->dpdma, DP_AUDIO_DMA_CHANNEL(0),
+ &s->audio_buffer_0);
+ xlnx_dpdma_set_host_data_location(s->dpdma, DP_AUDIO_DMA_CHANNEL(1),
+ &s->audio_buffer_1);
+}
+
+static inline void xlnx_dp_audio_mix_buffer(XlnxDPState *s)
+{
+ /*
+ * Audio packets are signed and have this shape:
+ * | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 |
+ * | R3 | L3 | R2 | L2 | R1 | L1 | R0 | L0 |
+ *
+ * Output audio is 16bits saturated.
+ */
+ int i;
+
+ if ((s->audio_data_available[0]) && (xlnx_dp_audio_get_volume(s, 0))) {
+ for (i = 0; i < s->audio_data_available[0] / 2; i++) {
+ s->temp_buffer[i] = (int64_t)(s->audio_buffer_0[i])
+ * xlnx_dp_audio_get_volume(s, 0) / 8192;
+ }
+ s->byte_left = s->audio_data_available[0];
+ } else {
+ memset(s->temp_buffer, 0, s->audio_data_available[1] / 2);
+ }
+
+ if ((s->audio_data_available[1]) && (xlnx_dp_audio_get_volume(s, 1))) {
+ if ((s->audio_data_available[0] == 0)
+ || (s->audio_data_available[1] == s->audio_data_available[0])) {
+ for (i = 0; i < s->audio_data_available[1] / 2; i++) {
+ s->temp_buffer[i] += (int64_t)(s->audio_buffer_1[i])
+ * xlnx_dp_audio_get_volume(s, 1) / 8192;
+ }
+ s->byte_left = s->audio_data_available[1];
+ }
+ }
+
+ for (i = 0; i < s->byte_left / 2; i++) {
+ s->out_buffer[i] = MAX(-32767, MIN(s->temp_buffer[i], 32767));
+ }
+
+ s->data_ptr = 0;
+}
+
+static void xlnx_dp_audio_callback(void *opaque, int avail)
+{
+ /*
+ * Get some data from the DPDMA and compute these datas.
+ * Then wait for QEMU's audio subsystem to call this callback.
+ */
+ XlnxDPState *s = XLNX_DP(opaque);
+ size_t written = 0;
+
+ /* If there are already some data don't get more data. */
+ if (s->byte_left == 0) {
+ s->audio_data_available[0] = xlnx_dpdma_start_operation(s->dpdma, 4,
+ true);
+ s->audio_data_available[1] = xlnx_dpdma_start_operation(s->dpdma, 5,
+ true);
+ xlnx_dp_audio_mix_buffer(s);
+ }
+
+ /* Send the buffer through the audio. */
+ if (s->byte_left <= MAX_QEMU_BUFFER_SIZE) {
+ if (s->byte_left != 0) {
+ written = AUD_write(s->amixer_output_stream,
+ &s->out_buffer[s->data_ptr], s->byte_left);
+ } else {
+ /*
+ * There is nothing to play.. We don't have any data! Fill the
+ * buffer with zero's and send it.
+ */
+ written = 0;
+ memset(s->out_buffer, 0, 1024);
+ AUD_write(s->amixer_output_stream, s->out_buffer, 1024);
+ }
+ } else {
+ written = AUD_write(s->amixer_output_stream,
+ &s->out_buffer[s->data_ptr], MAX_QEMU_BUFFER_SIZE);
+ }
+ s->byte_left -= written;
+ s->data_ptr += written;
+}
+
+/*
+ * AUX channel related function.
+ */
+static void xlnx_dp_aux_clear_rx_fifo(XlnxDPState *s)
+{
+ fifo8_reset(&s->rx_fifo);
+}
+
+static void xlnx_dp_aux_push_rx_fifo(XlnxDPState *s, uint8_t *buf, size_t len)
+{
+ DPRINTF("Push %u data in rx_fifo\n", (unsigned)len);
+ fifo8_push_all(&s->rx_fifo, buf, len);
+}
+
+static uint8_t xlnx_dp_aux_pop_rx_fifo(XlnxDPState *s)
+{
+ uint8_t ret;
+
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ DPRINTF("rx_fifo underflow..\n");
+ abort();
+ }
+ ret = fifo8_pop(&s->rx_fifo);
+ DPRINTF("pop 0x%" PRIX8 " from rx_fifo.\n", ret);
+ return ret;
+}
+
+static void xlnx_dp_aux_clear_tx_fifo(XlnxDPState *s)
+{
+ fifo8_reset(&s->tx_fifo);
+}
+
+static void xlnx_dp_aux_push_tx_fifo(XlnxDPState *s, uint8_t val, size_t len)
+{
+ DPRINTF("Push %u data in tx_fifo\n", (unsigned)len);
+ fifo8_push_all(&s->tx_fifo, &val, len);
+}
+
+static uint8_t xlnx_dp_aux_pop_tx_fifo(XlnxDPState *s)
+{
+ uint8_t ret;
+
+ if (fifo8_is_empty(&s->tx_fifo)) {
+ DPRINTF("tx_fifo underflow..\n");
+ abort();
+ }
+ ret = fifo8_pop(&s->tx_fifo);
+ DPRINTF("pop 0x%2.2X from tx_fifo.\n", ret);
+ return ret;
+}
+
+static uint32_t xlnx_dp_aux_get_address(XlnxDPState *s)
+{
+ return s->core_registers[DP_AUX_ADDRESS];
+}
+
+/*
+ * Get command from the register.
+ */
+static void xlnx_dp_aux_set_command(XlnxDPState *s, uint32_t value)
+{
+ bool address_only = (value & AUX_ADDR_ONLY_MASK) != 0;
+ AUXCommand cmd = (value & AUX_COMMAND_MASK) >> AUX_COMMAND_SHIFT;
+ uint8_t nbytes = (value & AUX_COMMAND_NBYTES) + 1;
+ uint8_t buf[16];
+ int i;
+
+ /*
+ * When an address_only command is executed nothing happen to the fifo, so
+ * just make nbytes = 0.
+ */
+ if (address_only) {
+ nbytes = 0;
+ }
+
+ switch (cmd) {
+ case READ_AUX:
+ case READ_I2C:
+ case READ_I2C_MOT:
+ s->core_registers[DP_AUX_REPLY_CODE] = aux_request(s->aux_bus, cmd,
+ xlnx_dp_aux_get_address(s),
+ nbytes, buf);
+ s->core_registers[DP_REPLY_DATA_COUNT] = nbytes;
+
+ if (s->core_registers[DP_AUX_REPLY_CODE] == AUX_I2C_ACK) {
+ xlnx_dp_aux_push_rx_fifo(s, buf, nbytes);
+ }
+ break;
+ case WRITE_AUX:
+ case WRITE_I2C:
+ case WRITE_I2C_MOT:
+ for (i = 0; i < nbytes; i++) {
+ buf[i] = xlnx_dp_aux_pop_tx_fifo(s);
+ }
+ s->core_registers[DP_AUX_REPLY_CODE] = aux_request(s->aux_bus, cmd,
+ xlnx_dp_aux_get_address(s),
+ nbytes, buf);
+ xlnx_dp_aux_clear_tx_fifo(s);
+ break;
+ case WRITE_I2C_STATUS:
+ qemu_log_mask(LOG_UNIMP, "xlnx_dp: Write i2c status not implemented\n");
+ break;
+ default:
+ abort();
+ }
+
+ s->core_registers[DP_INTERRUPT_SIGNAL_STATE] |= 0x04;
+}
+
+static void xlnx_dp_set_dpdma(Object *obj, const char *name, Object *val,
+ Error **errp)
+{
+ XlnxDPState *s = XLNX_DP(obj);
+ if (s->console) {
+ DisplaySurface *surface = qemu_console_surface(s->console);
+ XlnxDPDMAState *dma = XLNX_DPDMA(val);
+ xlnx_dpdma_set_host_data_location(dma, DP_GRAPHIC_DMA_CHANNEL,
+ surface_data(surface));
+ }
+}
+
+static inline uint8_t xlnx_dp_global_alpha_value(XlnxDPState *s)
+{
+ return (s->vblend_registers[V_BLEND_SET_GLOBAL_ALPHA_REG] & 0x1FE) >> 1;
+}
+
+static inline bool xlnx_dp_global_alpha_enabled(XlnxDPState *s)
+{
+ /*
+ * If the alpha is totally opaque (255) we consider the alpha is disabled to
+ * reduce CPU consumption.
+ */
+ return ((xlnx_dp_global_alpha_value(s) != 0xFF) &&
+ ((s->vblend_registers[V_BLEND_SET_GLOBAL_ALPHA_REG] & 0x01) != 0));
+}
+
+static void xlnx_dp_recreate_surface(XlnxDPState *s)
+{
+ /*
+ * Two possibilities, if blending is enabled the console displays
+ * bout_plane, if not g_plane is displayed.
+ */
+ uint16_t width = s->core_registers[DP_MAIN_STREAM_HRES];
+ uint16_t height = s->core_registers[DP_MAIN_STREAM_VRES];
+ DisplaySurface *current_console_surface = qemu_console_surface(s->console);
+
+ if ((width != 0) && (height != 0)) {
+ /*
+ * As dpy_gfx_replace_surface calls qemu_free_displaysurface on the
+ * surface we need to be carefull and don't free the surface associated
+ * to the console or double free will happen.
+ */
+ if (s->bout_plane.surface != current_console_surface) {
+ qemu_free_displaysurface(s->bout_plane.surface);
+ }
+ if (s->v_plane.surface != current_console_surface) {
+ qemu_free_displaysurface(s->v_plane.surface);
+ }
+ if (s->g_plane.surface != current_console_surface) {
+ qemu_free_displaysurface(s->g_plane.surface);
+ }
+
+ s->g_plane.surface
+ = qemu_create_displaysurface_from(width, height,
+ s->g_plane.format, 0, NULL);
+ s->v_plane.surface
+ = qemu_create_displaysurface_from(width, height,
+ s->v_plane.format, 0, NULL);
+ if (xlnx_dp_global_alpha_enabled(s)) {
+ s->bout_plane.surface =
+ qemu_create_displaysurface_from(width,
+ height,
+ s->g_plane.format,
+ 0, NULL);
+ dpy_gfx_replace_surface(s->console, s->bout_plane.surface);
+ } else {
+ s->bout_plane.surface = NULL;
+ dpy_gfx_replace_surface(s->console, s->g_plane.surface);
+ }
+
+ xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL,
+ surface_data(s->g_plane.surface));
+ xlnx_dpdma_set_host_data_location(s->dpdma, DP_VIDEO_DMA_CHANNEL,
+ surface_data(s->v_plane.surface));
+ }
+}
+
+/*
+ * Change the graphic format of the surface.
+ */
+static void xlnx_dp_change_graphic_fmt(XlnxDPState *s)
+{
+ switch (s->avbufm_registers[AV_BUF_FORMAT] & DP_GRAPHIC_MASK) {
+ case DP_GRAPHIC_RGBA8888:
+ s->g_plane.format = PIXMAN_r8g8b8a8;
+ break;
+ case DP_GRAPHIC_ABGR8888:
+ s->g_plane.format = PIXMAN_a8b8g8r8;
+ break;
+ case DP_GRAPHIC_RGB565:
+ s->g_plane.format = PIXMAN_r5g6b5;
+ break;
+ case DP_GRAPHIC_RGB888:
+ s->g_plane.format = PIXMAN_r8g8b8;
+ break;
+ case DP_GRAPHIC_BGR888:
+ s->g_plane.format = PIXMAN_b8g8r8;
+ break;
+ default:
+ DPRINTF("error: unsupported graphic format %u.\n",
+ s->avbufm_registers[AV_BUF_FORMAT] & DP_GRAPHIC_MASK);
+ abort();
+ }
+
+ switch (s->avbufm_registers[AV_BUF_FORMAT] & DP_NL_VID_FMT_MASK) {
+ case 0:
+ s->v_plane.format = PIXMAN_x8b8g8r8;
+ break;
+ case DP_NL_VID_RGBA8880:
+ s->v_plane.format = PIXMAN_x8b8g8r8;
+ break;
+ default:
+ DPRINTF("error: unsupported video format %u.\n",
+ s->avbufm_registers[AV_BUF_FORMAT] & DP_NL_VID_FMT_MASK);
+ abort();
+ }
+
+ xlnx_dp_recreate_surface(s);
+}
+
+static void xlnx_dp_update_irq(XlnxDPState *s)
+{
+ uint32_t flags;
+
+ flags = s->core_registers[DP_INT_STATUS] & ~s->core_registers[DP_INT_MASK];
+ DPRINTF("update IRQ value = %" PRIx32 "\n", flags);
+ qemu_set_irq(s->irq, flags != 0);
+}
+
+static uint64_t xlnx_dp_read(void *opaque, hwaddr offset, unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+ uint64_t ret = 0;
+
+ offset = offset >> 2;
+
+ switch (offset) {
+ case DP_TX_USER_FIFO_OVERFLOW:
+ /* This register is cleared after a read */
+ ret = s->core_registers[DP_TX_USER_FIFO_OVERFLOW];
+ s->core_registers[DP_TX_USER_FIFO_OVERFLOW] = 0;
+ break;
+ case DP_AUX_REPLY_DATA:
+ ret = xlnx_dp_aux_pop_rx_fifo(s);
+ break;
+ case DP_INTERRUPT_SIGNAL_STATE:
+ /*
+ * XXX: Not sure it is the right thing to do actually.
+ * The register is not written by the device driver so it's stuck
+ * to 0x04.
+ */
+ ret = s->core_registers[DP_INTERRUPT_SIGNAL_STATE];
+ s->core_registers[DP_INTERRUPT_SIGNAL_STATE] &= ~0x04;
+ break;
+ case DP_AUX_WRITE_FIFO:
+ case DP_TX_AUDIO_INFO_DATA(0):
+ case DP_TX_AUDIO_INFO_DATA(1):
+ case DP_TX_AUDIO_INFO_DATA(2):
+ case DP_TX_AUDIO_INFO_DATA(3):
+ case DP_TX_AUDIO_INFO_DATA(4):
+ case DP_TX_AUDIO_INFO_DATA(5):
+ case DP_TX_AUDIO_INFO_DATA(6):
+ case DP_TX_AUDIO_INFO_DATA(7):
+ case DP_TX_AUDIO_EXT_DATA(0):
+ case DP_TX_AUDIO_EXT_DATA(1):
+ case DP_TX_AUDIO_EXT_DATA(2):
+ case DP_TX_AUDIO_EXT_DATA(3):
+ case DP_TX_AUDIO_EXT_DATA(4):
+ case DP_TX_AUDIO_EXT_DATA(5):
+ case DP_TX_AUDIO_EXT_DATA(6):
+ case DP_TX_AUDIO_EXT_DATA(7):
+ case DP_TX_AUDIO_EXT_DATA(8):
+ /* write only registers */
+ ret = 0;
+ break;
+ default:
+ assert(offset <= (0x3AC >> 2));
+ ret = s->core_registers[offset];
+ break;
+ }
+
+ DPRINTF("core read @%" PRIx64 " = 0x%8.8" PRIX64 "\n", offset << 2, ret);
+ return ret;
+}
+
+static void xlnx_dp_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ DPRINTF("core write @%" PRIx64 " = 0x%8.8" PRIX64 "\n", offset, value);
+
+ offset = offset >> 2;
+
+ switch (offset) {
+ /*
+ * Only special write case are handled.
+ */
+ case DP_LINK_BW_SET:
+ s->core_registers[offset] = value & 0x000000FF;
+ break;
+ case DP_LANE_COUNT_SET:
+ case DP_MAIN_STREAM_MISC0:
+ s->core_registers[offset] = value & 0x0000000F;
+ break;
+ case DP_TRAINING_PATTERN_SET:
+ case DP_LINK_QUAL_PATTERN_SET:
+ case DP_MAIN_STREAM_POLARITY:
+ case DP_PHY_VOLTAGE_DIFF_LANE_0:
+ case DP_PHY_VOLTAGE_DIFF_LANE_1:
+ s->core_registers[offset] = value & 0x00000003;
+ break;
+ case DP_ENHANCED_FRAME_EN:
+ case DP_SCRAMBLING_DISABLE:
+ case DP_DOWNSPREAD_CTRL:
+ case DP_MAIN_STREAM_ENABLE:
+ case DP_TRANSMIT_PRBS7:
+ s->core_registers[offset] = value & 0x00000001;
+ break;
+ case DP_PHY_CLOCK_SELECT:
+ s->core_registers[offset] = value & 0x00000007;
+ break;
+ case DP_SOFTWARE_RESET:
+ /*
+ * No need to update this bit as it's read '0'.
+ */
+ /*
+ * TODO: reset IP.
+ */
+ break;
+ case DP_TRANSMITTER_ENABLE:
+ s->core_registers[offset] = value & 0x01;
+ break;
+ case DP_FORCE_SCRAMBLER_RESET:
+ /*
+ * No need to update this bit as it's read '0'.
+ */
+ /*
+ * TODO: force a scrambler reset??
+ */
+ break;
+ case DP_AUX_COMMAND_REGISTER:
+ s->core_registers[offset] = value & 0x00001F0F;
+ xlnx_dp_aux_set_command(s, s->core_registers[offset]);
+ break;
+ case DP_MAIN_STREAM_HTOTAL:
+ case DP_MAIN_STREAM_VTOTAL:
+ case DP_MAIN_STREAM_HSTART:
+ case DP_MAIN_STREAM_VSTART:
+ s->core_registers[offset] = value & 0x0000FFFF;
+ break;
+ case DP_MAIN_STREAM_HRES:
+ case DP_MAIN_STREAM_VRES:
+ s->core_registers[offset] = value & 0x0000FFFF;
+ xlnx_dp_recreate_surface(s);
+ break;
+ case DP_MAIN_STREAM_HSWIDTH:
+ case DP_MAIN_STREAM_VSWIDTH:
+ s->core_registers[offset] = value & 0x00007FFF;
+ break;
+ case DP_MAIN_STREAM_MISC1:
+ s->core_registers[offset] = value & 0x00000086;
+ break;
+ case DP_MAIN_STREAM_M_VID:
+ case DP_MAIN_STREAM_N_VID:
+ s->core_registers[offset] = value & 0x00FFFFFF;
+ break;
+ case DP_MSA_TRANSFER_UNIT_SIZE:
+ case DP_MIN_BYTES_PER_TU:
+ case DP_INIT_WAIT:
+ s->core_registers[offset] = value & 0x00000007;
+ break;
+ case DP_USER_DATA_COUNT_PER_LANE:
+ s->core_registers[offset] = value & 0x0003FFFF;
+ break;
+ case DP_FRAC_BYTES_PER_TU:
+ s->core_registers[offset] = value & 0x000003FF;
+ break;
+ case DP_PHY_RESET:
+ s->core_registers[offset] = value & 0x00010003;
+ /*
+ * TODO: Reset something?
+ */
+ break;
+ case DP_TX_PHY_POWER_DOWN:
+ s->core_registers[offset] = value & 0x0000000F;
+ /*
+ * TODO: Power down things?
+ */
+ break;
+ case DP_AUX_WRITE_FIFO:
+ xlnx_dp_aux_push_tx_fifo(s, value, 1);
+ break;
+ case DP_AUX_CLOCK_DIVIDER:
+ break;
+ case DP_AUX_REPLY_COUNT:
+ /*
+ * Writing to this register clear the counter.
+ */
+ s->core_registers[offset] = 0x00000000;
+ break;
+ case DP_AUX_ADDRESS:
+ s->core_registers[offset] = value & 0x000FFFFF;
+ break;
+ case DP_VERSION_REGISTER:
+ case DP_CORE_ID:
+ case DP_TX_USER_FIFO_OVERFLOW:
+ case DP_AUX_REPLY_DATA:
+ case DP_AUX_REPLY_CODE:
+ case DP_REPLY_DATA_COUNT:
+ case DP_REPLY_STATUS:
+ case DP_HPD_DURATION:
+ /*
+ * Write to read only location..
+ */
+ break;
+ case DP_TX_AUDIO_CONTROL:
+ s->core_registers[offset] = value & 0x00000001;
+ xlnx_dp_audio_activate(s);
+ break;
+ case DP_TX_AUDIO_CHANNELS:
+ s->core_registers[offset] = value & 0x00000007;
+ xlnx_dp_audio_activate(s);
+ break;
+ case DP_INT_STATUS:
+ s->core_registers[DP_INT_STATUS] &= ~value;
+ xlnx_dp_update_irq(s);
+ break;
+ case DP_INT_EN:
+ s->core_registers[DP_INT_MASK] &= ~value;
+ xlnx_dp_update_irq(s);
+ break;
+ case DP_INT_DS:
+ s->core_registers[DP_INT_MASK] |= ~value;
+ xlnx_dp_update_irq(s);
+ break;
+ default:
+ assert(offset <= (0x504C >> 2));
+ s->core_registers[offset] = value;
+ break;
+ }
+}
+
+static const MemoryRegionOps dp_ops = {
+ .read = xlnx_dp_read,
+ .write = xlnx_dp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+/*
+ * This is to handle Read/Write to the Video Blender.
+ */
+static void xlnx_dp_vblend_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+ bool alpha_was_enabled;
+
+ DPRINTF("vblend: write @0x%" HWADDR_PRIX " = 0x%" PRIX32 "\n", offset,
+ (uint32_t)value);
+ offset = offset >> 2;
+
+ switch (offset) {
+ case V_BLEND_BG_CLR_0:
+ case V_BLEND_BG_CLR_1:
+ case V_BLEND_BG_CLR_2:
+ s->vblend_registers[offset] = value & 0x00000FFF;
+ break;
+ case V_BLEND_SET_GLOBAL_ALPHA_REG:
+ /*
+ * A write to this register can enable or disable blending. Thus we need
+ * to recreate the surfaces.
+ */
+ alpha_was_enabled = xlnx_dp_global_alpha_enabled(s);
+ s->vblend_registers[offset] = value & 0x000001FF;
+ if (xlnx_dp_global_alpha_enabled(s) != alpha_was_enabled) {
+ xlnx_dp_recreate_surface(s);
+ }
+ break;
+ case V_BLEND_OUTPUT_VID_FORMAT:
+ s->vblend_registers[offset] = value & 0x00000017;
+ break;
+ case V_BLEND_LAYER0_CONTROL:
+ case V_BLEND_LAYER1_CONTROL:
+ s->vblend_registers[offset] = value & 0x00000103;
+ break;
+ case V_BLEND_RGB2YCBCR_COEFF(0):
+ case V_BLEND_RGB2YCBCR_COEFF(1):
+ case V_BLEND_RGB2YCBCR_COEFF(2):
+ case V_BLEND_RGB2YCBCR_COEFF(3):
+ case V_BLEND_RGB2YCBCR_COEFF(4):
+ case V_BLEND_RGB2YCBCR_COEFF(5):
+ case V_BLEND_RGB2YCBCR_COEFF(6):
+ case V_BLEND_RGB2YCBCR_COEFF(7):
+ case V_BLEND_RGB2YCBCR_COEFF(8):
+ case V_BLEND_IN1CSC_COEFF(0):
+ case V_BLEND_IN1CSC_COEFF(1):
+ case V_BLEND_IN1CSC_COEFF(2):
+ case V_BLEND_IN1CSC_COEFF(3):
+ case V_BLEND_IN1CSC_COEFF(4):
+ case V_BLEND_IN1CSC_COEFF(5):
+ case V_BLEND_IN1CSC_COEFF(6):
+ case V_BLEND_IN1CSC_COEFF(7):
+ case V_BLEND_IN1CSC_COEFF(8):
+ case V_BLEND_IN2CSC_COEFF(0):
+ case V_BLEND_IN2CSC_COEFF(1):
+ case V_BLEND_IN2CSC_COEFF(2):
+ case V_BLEND_IN2CSC_COEFF(3):
+ case V_BLEND_IN2CSC_COEFF(4):
+ case V_BLEND_IN2CSC_COEFF(5):
+ case V_BLEND_IN2CSC_COEFF(6):
+ case V_BLEND_IN2CSC_COEFF(7):
+ case V_BLEND_IN2CSC_COEFF(8):
+ s->vblend_registers[offset] = value & 0x0000FFFF;
+ break;
+ case V_BLEND_LUMA_IN1CSC_OFFSET:
+ case V_BLEND_CR_IN1CSC_OFFSET:
+ case V_BLEND_CB_IN1CSC_OFFSET:
+ case V_BLEND_LUMA_IN2CSC_OFFSET:
+ case V_BLEND_CR_IN2CSC_OFFSET:
+ case V_BLEND_CB_IN2CSC_OFFSET:
+ case V_BLEND_LUMA_OUTCSC_OFFSET:
+ case V_BLEND_CR_OUTCSC_OFFSET:
+ case V_BLEND_CB_OUTCSC_OFFSET:
+ s->vblend_registers[offset] = value & 0x3FFF7FFF;
+ break;
+ case V_BLEND_CHROMA_KEY_ENABLE:
+ s->vblend_registers[offset] = value & 0x00000003;
+ break;
+ case V_BLEND_CHROMA_KEY_COMP1:
+ case V_BLEND_CHROMA_KEY_COMP2:
+ case V_BLEND_CHROMA_KEY_COMP3:
+ s->vblend_registers[offset] = value & 0x0FFF0FFF;
+ break;
+ default:
+ s->vblend_registers[offset] = value;
+ break;
+ }
+}
+
+static uint64_t xlnx_dp_vblend_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ DPRINTF("vblend: read @0x%" HWADDR_PRIX " = 0x%" PRIX32 "\n", offset,
+ s->vblend_registers[offset >> 2]);
+ return s->vblend_registers[offset >> 2];
+}
+
+static const MemoryRegionOps vblend_ops = {
+ .read = xlnx_dp_vblend_read,
+ .write = xlnx_dp_vblend_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+/*
+ * This is to handle Read/Write to the Audio Video buffer manager.
+ */
+static void xlnx_dp_avbufm_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ DPRINTF("avbufm: write @0x%" HWADDR_PRIX " = 0x%" PRIX32 "\n", offset,
+ (uint32_t)value);
+ offset = offset >> 2;
+
+ switch (offset) {
+ case AV_BUF_FORMAT:
+ s->avbufm_registers[offset] = value & 0x00000FFF;
+ xlnx_dp_change_graphic_fmt(s);
+ break;
+ case AV_CHBUF0:
+ case AV_CHBUF1:
+ case AV_CHBUF2:
+ case AV_CHBUF3:
+ case AV_CHBUF4:
+ case AV_CHBUF5:
+ s->avbufm_registers[offset] = value & 0x0000007F;
+ break;
+ case AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT:
+ s->avbufm_registers[offset] = value & 0x0000007F;
+ break;
+ case AV_BUF_DITHER_CONFIG:
+ s->avbufm_registers[offset] = value & 0x000007FF;
+ break;
+ case AV_BUF_DITHER_CONFIG_MAX:
+ case AV_BUF_DITHER_CONFIG_MIN:
+ s->avbufm_registers[offset] = value & 0x00000FFF;
+ break;
+ case AV_BUF_PATTERN_GEN_SELECT:
+ s->avbufm_registers[offset] = value & 0xFFFFFF03;
+ break;
+ case AV_BUF_AUD_VID_CLK_SOURCE:
+ s->avbufm_registers[offset] = value & 0x00000007;
+ break;
+ case AV_BUF_SRST_REG:
+ s->avbufm_registers[offset] = value & 0x00000002;
+ break;
+ case AV_BUF_AUDIO_CH_CONFIG:
+ s->avbufm_registers[offset] = value & 0x00000003;
+ break;
+ case AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(0):
+ case AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(1):
+ case AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(2):
+ case AV_BUF_VIDEO_COMP_SCALE_FACTOR(0):
+ case AV_BUF_VIDEO_COMP_SCALE_FACTOR(1):
+ case AV_BUF_VIDEO_COMP_SCALE_FACTOR(2):
+ s->avbufm_registers[offset] = value & 0x0000FFFF;
+ break;
+ case AV_BUF_LIVE_VIDEO_COMP_SF(0):
+ case AV_BUF_LIVE_VIDEO_COMP_SF(1):
+ case AV_BUF_LIVE_VIDEO_COMP_SF(2):
+ case AV_BUF_LIVE_VID_CONFIG:
+ case AV_BUF_LIVE_GFX_COMP_SF(0):
+ case AV_BUF_LIVE_GFX_COMP_SF(1):
+ case AV_BUF_LIVE_GFX_COMP_SF(2):
+ case AV_BUF_LIVE_GFX_CONFIG:
+ case AV_BUF_NON_LIVE_LATENCY:
+ case AV_BUF_STC_CONTROL:
+ case AV_BUF_STC_INIT_VALUE0:
+ case AV_BUF_STC_INIT_VALUE1:
+ case AV_BUF_STC_ADJ:
+ case AV_BUF_STC_VIDEO_VSYNC_TS_REG0:
+ case AV_BUF_STC_VIDEO_VSYNC_TS_REG1:
+ case AV_BUF_STC_EXT_VSYNC_TS_REG0:
+ case AV_BUF_STC_EXT_VSYNC_TS_REG1:
+ case AV_BUF_STC_CUSTOM_EVENT_TS_REG0:
+ case AV_BUF_STC_CUSTOM_EVENT_TS_REG1:
+ case AV_BUF_STC_CUSTOM_EVENT2_TS_REG0:
+ case AV_BUF_STC_CUSTOM_EVENT2_TS_REG1:
+ case AV_BUF_STC_SNAPSHOT0:
+ case AV_BUF_STC_SNAPSHOT1:
+ case AV_BUF_HCOUNT_VCOUNT_INT0:
+ case AV_BUF_HCOUNT_VCOUNT_INT1:
+ qemu_log_mask(LOG_UNIMP, "avbufm: unimplmented");
+ break;
+ default:
+ s->avbufm_registers[offset] = value;
+ break;
+ }
+}
+
+static uint64_t xlnx_dp_avbufm_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ offset = offset >> 2;
+ return s->avbufm_registers[offset];
+}
+
+static const MemoryRegionOps avbufm_ops = {
+ .read = xlnx_dp_avbufm_read,
+ .write = xlnx_dp_avbufm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+/*
+ * This is a global alpha blending using pixman.
+ * Both graphic and video planes are multiplied with the global alpha
+ * coefficient and added.
+ */
+static inline void xlnx_dp_blend_surface(XlnxDPState *s)
+{
+ pixman_fixed_t alpha1[] = { pixman_double_to_fixed(1),
+ pixman_double_to_fixed(1),
+ pixman_double_to_fixed(1.0) };
+ pixman_fixed_t alpha2[] = { pixman_double_to_fixed(1),
+ pixman_double_to_fixed(1),
+ pixman_double_to_fixed(1.0) };
+
+ if ((surface_width(s->g_plane.surface)
+ != surface_width(s->v_plane.surface)) ||
+ (surface_height(s->g_plane.surface)
+ != surface_height(s->v_plane.surface))) {
+ return;
+ }
+
+ alpha1[2] = pixman_double_to_fixed((double)(xlnx_dp_global_alpha_value(s))
+ / 256.0);
+ alpha2[2] = pixman_double_to_fixed((255.0
+ - (double)xlnx_dp_global_alpha_value(s))
+ / 256.0);
+
+ pixman_image_set_filter(s->g_plane.surface->image,
+ PIXMAN_FILTER_CONVOLUTION, alpha1, 3);
+ pixman_image_composite(PIXMAN_OP_SRC, s->g_plane.surface->image, 0,
+ s->bout_plane.surface->image, 0, 0, 0, 0, 0, 0,
+ surface_width(s->g_plane.surface),
+ surface_height(s->g_plane.surface));
+ pixman_image_set_filter(s->v_plane.surface->image,
+ PIXMAN_FILTER_CONVOLUTION, alpha2, 3);
+ pixman_image_composite(PIXMAN_OP_ADD, s->v_plane.surface->image, 0,
+ s->bout_plane.surface->image, 0, 0, 0, 0, 0, 0,
+ surface_width(s->g_plane.surface),
+ surface_height(s->g_plane.surface));
+}
+
+static void xlnx_dp_update_display(void *opaque)
+{
+ XlnxDPState *s = XLNX_DP(opaque);
+
+ if ((s->core_registers[DP_TRANSMITTER_ENABLE] & 0x01) == 0) {
+ return;
+ }
+
+ s->core_registers[DP_INT_STATUS] |= (1 << 13);
+ xlnx_dp_update_irq(s);
+
+ xlnx_dpdma_trigger_vsync_irq(s->dpdma);
+
+ /*
+ * Trigger the DMA channel.
+ */
+ if (!xlnx_dpdma_start_operation(s->dpdma, 3, false)) {
+ /*
+ * An error occured don't do anything with the data..
+ * Trigger an underflow interrupt.
+ */
+ s->core_registers[DP_INT_STATUS] |= (1 << 21);
+ xlnx_dp_update_irq(s);
+ return;
+ }
+
+ if (xlnx_dp_global_alpha_enabled(s)) {
+ if (!xlnx_dpdma_start_operation(s->dpdma, 0, false)) {
+ s->core_registers[DP_INT_STATUS] |= (1 << 21);
+ xlnx_dp_update_irq(s);
+ return;
+ }
+ xlnx_dp_blend_surface(s);
+ }
+
+ /*
+ * XXX: We might want to update only what changed.
+ */
+ dpy_gfx_update(s->console, 0, 0, surface_width(s->g_plane.surface),
+ surface_height(s->g_plane.surface));
+}
+
+static const GraphicHwOps xlnx_dp_gfx_ops = {
+ .gfx_update = xlnx_dp_update_display,
+};
+
+static void xlnx_dp_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ XlnxDPState *s = XLNX_DP(obj);
+
+ memory_region_init(&s->container, obj, TYPE_XLNX_DP, 0xC050);
+
+ memory_region_init_io(&s->core_iomem, obj, &dp_ops, s, TYPE_XLNX_DP
+ ".core", 0x3AF);
+ memory_region_add_subregion(&s->container, 0x0000, &s->core_iomem);
+
+ memory_region_init_io(&s->vblend_iomem, obj, &vblend_ops, s, TYPE_XLNX_DP
+ ".v_blend", 0x1DF);
+ memory_region_add_subregion(&s->container, 0xA000, &s->vblend_iomem);
+
+ memory_region_init_io(&s->avbufm_iomem, obj, &avbufm_ops, s, TYPE_XLNX_DP
+ ".av_buffer_manager", 0x238);
+ memory_region_add_subregion(&s->container, 0xB000, &s->avbufm_iomem);
+
+ memory_region_init_io(&s->audio_iomem, obj, &audio_ops, s, TYPE_XLNX_DP
+ ".audio", sizeof(s->audio_registers));
+ memory_region_add_subregion(&s->container, 0xC000, &s->audio_iomem);
+
+ sysbus_init_mmio(sbd, &s->container);
+ sysbus_init_irq(sbd, &s->irq);
+
+ object_property_add_link(obj, "dpdma", TYPE_XLNX_DPDMA,
+ (Object **) &s->dpdma,
+ xlnx_dp_set_dpdma,
+ OBJ_PROP_LINK_UNREF_ON_RELEASE,
+ &error_abort);
+
+ /*
+ * Initialize AUX Bus.
+ */
+ s->aux_bus = aux_init_bus(DEVICE(obj), "aux");
+
+ /*
+ * Initialize DPCD and EDID..
+ */
+ s->dpcd = DPCD(aux_create_slave(s->aux_bus, "dpcd", 0x00000));
+ s->edid = I2CDDC(qdev_create(BUS(aux_get_i2c_bus(s->aux_bus)), "i2c-ddc"));
+ i2c_set_slave_address(I2C_SLAVE(s->edid), 0x50);
+
+ fifo8_create(&s->rx_fifo, 16);
+ fifo8_create(&s->tx_fifo, 16);
+}
+
+static void xlnx_dp_realize(DeviceState *dev, Error **errp)
+{
+ XlnxDPState *s = XLNX_DP(dev);
+ DisplaySurface *surface;
+ struct audsettings as;
+
+ s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s);
+ surface = qemu_console_surface(s->console);
+ xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL,
+ surface_data(surface));
+
+ as.freq = 44100;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = 0;
+
+ AUD_register_card("xlnx_dp.audio", &s->aud_card);
+
+ s->amixer_output_stream = AUD_open_out(&s->aud_card,
+ s->amixer_output_stream,
+ "xlnx_dp.audio.out",
+ s,
+ xlnx_dp_audio_callback,
+ &as);
+ AUD_set_volume_out(s->amixer_output_stream, 0, 255, 255);
+ xlnx_dp_audio_activate(s);
+}
+
+static void xlnx_dp_reset(DeviceState *dev)
+{
+ XlnxDPState *s = XLNX_DP(dev);
+
+ memset(s->core_registers, 0, sizeof(s->core_registers));
+ s->core_registers[DP_VERSION_REGISTER] = 0x04010000;
+ s->core_registers[DP_CORE_ID] = 0x01020000;
+ s->core_registers[DP_REPLY_STATUS] = 0x00000010;
+ s->core_registers[DP_MSA_TRANSFER_UNIT_SIZE] = 0x00000040;
+ s->core_registers[DP_INIT_WAIT] = 0x00000020;
+ s->core_registers[DP_PHY_RESET] = 0x00010003;
+ s->core_registers[DP_INT_MASK] = 0xFFFFF03F;
+ s->core_registers[DP_PHY_STATUS] = 0x00000043;
+ s->core_registers[DP_INTERRUPT_SIGNAL_STATE] = 0x00000001;
+
+ s->vblend_registers[V_BLEND_RGB2YCBCR_COEFF(0)] = 0x00001000;
+ s->vblend_registers[V_BLEND_RGB2YCBCR_COEFF(4)] = 0x00001000;
+ s->vblend_registers[V_BLEND_RGB2YCBCR_COEFF(8)] = 0x00001000;
+ s->vblend_registers[V_BLEND_IN1CSC_COEFF(0)] = 0x00001000;
+ s->vblend_registers[V_BLEND_IN1CSC_COEFF(4)] = 0x00001000;
+ s->vblend_registers[V_BLEND_IN1CSC_COEFF(8)] = 0x00001000;
+ s->vblend_registers[V_BLEND_IN2CSC_COEFF(0)] = 0x00001000;
+ s->vblend_registers[V_BLEND_IN2CSC_COEFF(4)] = 0x00001000;
+ s->vblend_registers[V_BLEND_IN2CSC_COEFF(8)] = 0x00001000;
+
+ s->avbufm_registers[AV_BUF_NON_LIVE_LATENCY] = 0x00000180;
+ s->avbufm_registers[AV_BUF_OUTPUT_AUDIO_VIDEO_SELECT] = 0x00000008;
+ s->avbufm_registers[AV_BUF_DITHER_CONFIG_MAX] = 0x00000FFF;
+ s->avbufm_registers[AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(0)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(1)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_GRAPHICS_COMP_SCALE_FACTOR(2)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_VIDEO_COMP_SCALE_FACTOR(0)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_VIDEO_COMP_SCALE_FACTOR(1)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_VIDEO_COMP_SCALE_FACTOR(2)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_LIVE_VIDEO_COMP_SF(0)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_LIVE_VIDEO_COMP_SF(1)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_LIVE_VIDEO_COMP_SF(2)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_LIVE_GFX_COMP_SF(0)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_LIVE_GFX_COMP_SF(1)] = 0x00010101;
+ s->avbufm_registers[AV_BUF_LIVE_GFX_COMP_SF(2)] = 0x00010101;
+
+ memset(s->audio_registers, 0, sizeof(s->audio_registers));
+ s->byte_left = 0;
+
+ xlnx_dp_aux_clear_rx_fifo(s);
+ xlnx_dp_change_graphic_fmt(s);
+ xlnx_dp_update_irq(s);
+}
+
+static void xlnx_dp_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = xlnx_dp_realize;
+ dc->vmsd = &vmstate_dp;
+ dc->reset = xlnx_dp_reset;
+}
+
+static const TypeInfo xlnx_dp_info = {
+ .name = TYPE_XLNX_DP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XlnxDPState),
+ .instance_init = xlnx_dp_init,
+ .class_init = xlnx_dp_class_init,
+};
+
+static void xlnx_dp_register_types(void)
+{
+ type_register_static(&xlnx_dp_info);
+}
+
+type_init(xlnx_dp_register_types)
diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index a1abbcf..8b0823e 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -8,6 +8,7 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
common-obj-$(CONFIG_STP2000) += sparc32_dma.o
common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
+obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c
index 2306abc..634a432 100644
--- a/hw/dma/pxa2xx_dma.c
+++ b/hw/dma/pxa2xx_dma.c
@@ -12,6 +12,7 @@
#include "hw/hw.h"
#include "hw/arm/pxa.h"
#include "hw/sysbus.h"
+#include "qapi/error.h"
#define PXA255_DMA_NUM_CHANNELS 16
#define PXA27X_DMA_NUM_CHANNELS 32
@@ -450,31 +451,36 @@ static void pxa2xx_dma_request(void *opaque, int req_num, int on)
}
}
-static int pxa2xx_dma_init(SysBusDevice *sbd)
+static void pxa2xx_dma_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ PXA2xxDMAState *s = PXA2XX_DMA(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS);
+
+ qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS);
+
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_dma_ops, s,
+ "pxa2xx.dma", 0x00010000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void pxa2xx_dma_realize(DeviceState *dev, Error **errp)
{
- DeviceState *dev = DEVICE(sbd);
PXA2xxDMAState *s = PXA2XX_DMA(dev);
int i;
if (s->channels <= 0) {
- return -1;
+ error_setg(errp, "channels value invalid");
+ return;
}
s->chan = g_new0(PXA2xxDMAChannel, s->channels);
for (i = 0; i < s->channels; i ++)
s->chan[i].state = DCSR_STOPINTR;
-
- memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS);
-
- qdev_init_gpio_in(dev, pxa2xx_dma_request, PXA2XX_DMA_NUM_REQUESTS);
-
- memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_dma_ops, s,
- "pxa2xx.dma", 0x00010000);
- sysbus_init_mmio(sbd, &s->iomem);
- sysbus_init_irq(sbd, &s->irq);
-
- return 0;
}
DeviceState *pxa27x_dma_init(hwaddr base, qemu_irq irq)
@@ -553,18 +559,18 @@ static Property pxa2xx_dma_properties[] = {
static void pxa2xx_dma_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pxa2xx_dma_init;
dc->desc = "PXA2xx DMA controller";
dc->vmsd = &vmstate_pxa2xx_dma;
dc->props = pxa2xx_dma_properties;
+ dc->realize = pxa2xx_dma_realize;
}
static const TypeInfo pxa2xx_dma_info = {
.name = TYPE_PXA2XX_DMA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PXA2xxDMAState),
+ .instance_init = pxa2xx_dma_init,
.class_init = pxa2xx_dma_class_init,
};
diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c
new file mode 100644
index 0000000..8ceb21d
--- /dev/null
+++ b/hw/dma/xlnx_dpdma.c
@@ -0,0 +1,786 @@
+/*
+ * xlnx_dpdma.c
+ *
+ * Copyright (C) 2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/dma/xlnx_dpdma.h"
+
+#ifndef DEBUG_DPDMA
+#define DEBUG_DPDMA 0
+#endif
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_DPDMA) { \
+ qemu_log("xlnx_dpdma: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0);
+
+/*
+ * Registers offset for DPDMA.
+ */
+#define DPDMA_ERR_CTRL (0x0000)
+#define DPDMA_ISR (0x0004 >> 2)
+#define DPDMA_IMR (0x0008 >> 2)
+#define DPDMA_IEN (0x000C >> 2)
+#define DPDMA_IDS (0x0010 >> 2)
+#define DPDMA_EISR (0x0014 >> 2)
+#define DPDMA_EIMR (0x0018 >> 2)
+#define DPDMA_EIEN (0x001C >> 2)
+#define DPDMA_EIDS (0x0020 >> 2)
+#define DPDMA_CNTL (0x0100 >> 2)
+
+#define DPDMA_GBL (0x0104 >> 2)
+#define DPDMA_GBL_TRG_CH(n) (1 << n)
+#define DPDMA_GBL_RTRG_CH(n) (1 << 6 << n)
+
+#define DPDMA_ALC0_CNTL (0x0108 >> 2)
+#define DPDMA_ALC0_STATUS (0x010C >> 2)
+#define DPDMA_ALC0_MAX (0x0110 >> 2)
+#define DPDMA_ALC0_MIN (0x0114 >> 2)
+#define DPDMA_ALC0_ACC (0x0118 >> 2)
+#define DPDMA_ALC0_ACC_TRAN (0x011C >> 2)
+#define DPDMA_ALC1_CNTL (0x0120 >> 2)
+#define DPDMA_ALC1_STATUS (0x0124 >> 2)
+#define DPDMA_ALC1_MAX (0x0128 >> 2)
+#define DPDMA_ALC1_MIN (0x012C >> 2)
+#define DPDMA_ALC1_ACC (0x0130 >> 2)
+#define DPDMA_ALC1_ACC_TRAN (0x0134 >> 2)
+
+#define DPDMA_DSCR_STRT_ADDRE_CH(n) ((0x0200 + n * 0x100) >> 2)
+#define DPDMA_DSCR_STRT_ADDR_CH(n) ((0x0204 + n * 0x100) >> 2)
+#define DPDMA_DSCR_NEXT_ADDRE_CH(n) ((0x0208 + n * 0x100) >> 2)
+#define DPDMA_DSCR_NEXT_ADDR_CH(n) ((0x020C + n * 0x100) >> 2)
+#define DPDMA_PYLD_CUR_ADDRE_CH(n) ((0x0210 + n * 0x100) >> 2)
+#define DPDMA_PYLD_CUR_ADDR_CH(n) ((0x0214 + n * 0x100) >> 2)
+
+#define DPDMA_CNTL_CH(n) ((0x0218 + n * 0x100) >> 2)
+#define DPDMA_CNTL_CH_EN (1)
+#define DPDMA_CNTL_CH_PAUSED (1 << 1)
+
+#define DPDMA_STATUS_CH(n) ((0x021C + n * 0x100) >> 2)
+#define DPDMA_STATUS_BURST_TYPE (1 << 4)
+#define DPDMA_STATUS_MODE (1 << 5)
+#define DPDMA_STATUS_EN_CRC (1 << 6)
+#define DPDMA_STATUS_LAST_DSCR (1 << 7)
+#define DPDMA_STATUS_LDSCR_FRAME (1 << 8)
+#define DPDMA_STATUS_IGNR_DONE (1 << 9)
+#define DPDMA_STATUS_DSCR_DONE (1 << 10)
+#define DPDMA_STATUS_EN_DSCR_UP (1 << 11)
+#define DPDMA_STATUS_EN_DSCR_INTR (1 << 12)
+#define DPDMA_STATUS_PREAMBLE_OFF (13)
+
+#define DPDMA_VDO_CH(n) ((0x0220 + n * 0x100) >> 2)
+#define DPDMA_PYLD_SZ_CH(n) ((0x0224 + n * 0x100) >> 2)
+#define DPDMA_DSCR_ID_CH(n) ((0x0228 + n * 0x100) >> 2)
+
+/*
+ * Descriptor control field.
+ */
+#define CONTROL_PREAMBLE_VALUE 0xA5
+
+#define DSCR_CTRL_PREAMBLE 0xFF
+#define DSCR_CTRL_EN_DSCR_DONE_INTR (1 << 8)
+#define DSCR_CTRL_EN_DSCR_UPDATE (1 << 9)
+#define DSCR_CTRL_IGNORE_DONE (1 << 10)
+#define DSCR_CTRL_AXI_BURST_TYPE (1 << 11)
+#define DSCR_CTRL_AXCACHE (0x0F << 12)
+#define DSCR_CTRL_AXPROT (0x2 << 16)
+#define DSCR_CTRL_DESCRIPTOR_MODE (1 << 18)
+#define DSCR_CTRL_LAST_DESCRIPTOR (1 << 19)
+#define DSCR_CTRL_ENABLE_CRC (1 << 20)
+#define DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME (1 << 21)
+
+/*
+ * Descriptor timestamp field.
+ */
+#define STATUS_DONE (1 << 31)
+
+#define DPDMA_FRAG_MAX_SZ (4096)
+
+enum DPDMABurstType {
+ DPDMA_INCR = 0,
+ DPDMA_FIXED = 1
+};
+
+enum DPDMAMode {
+ DPDMA_CONTIGOUS = 0,
+ DPDMA_FRAGMENTED = 1
+};
+
+struct DPDMADescriptor {
+ uint32_t control;
+ uint32_t descriptor_id;
+ /* transfer size in byte. */
+ uint32_t xfer_size;
+ uint32_t line_size_stride;
+ uint32_t timestamp_lsb;
+ uint32_t timestamp_msb;
+ /* contains extension for both descriptor and source. */
+ uint32_t address_extension;
+ uint32_t next_descriptor;
+ uint32_t source_address;
+ uint32_t address_extension_23;
+ uint32_t address_extension_45;
+ uint32_t source_address2;
+ uint32_t source_address3;
+ uint32_t source_address4;
+ uint32_t source_address5;
+ uint32_t crc;
+};
+
+typedef enum DPDMABurstType DPDMABurstType;
+typedef enum DPDMAMode DPDMAMode;
+typedef struct DPDMADescriptor DPDMADescriptor;
+
+static bool xlnx_dpdma_desc_is_last(DPDMADescriptor *desc)
+{
+ return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0);
+}
+
+static bool xlnx_dpdma_desc_is_last_of_frame(DPDMADescriptor *desc)
+{
+ return ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0);
+}
+
+static uint64_t xlnx_dpdma_desc_get_source_address(DPDMADescriptor *desc,
+ uint8_t frag)
+{
+ uint64_t addr = 0;
+ assert(frag < 5);
+
+ switch (frag) {
+ case 0:
+ addr = desc->source_address
+ + (extract32(desc->address_extension, 16, 12) << 20);
+ break;
+ case 1:
+ addr = desc->source_address2
+ + (extract32(desc->address_extension_23, 0, 12) << 8);
+ break;
+ case 2:
+ addr = desc->source_address3
+ + (extract32(desc->address_extension_23, 16, 12) << 20);
+ break;
+ case 3:
+ addr = desc->source_address4
+ + (extract32(desc->address_extension_45, 0, 12) << 8);
+ break;
+ case 4:
+ addr = desc->source_address5
+ + (extract32(desc->address_extension_45, 16, 12) << 20);
+ break;
+ default:
+ addr = 0;
+ break;
+ }
+
+ return addr;
+}
+
+static uint32_t xlnx_dpdma_desc_get_transfer_size(DPDMADescriptor *desc)
+{
+ return desc->xfer_size;
+}
+
+static uint32_t xlnx_dpdma_desc_get_line_size(DPDMADescriptor *desc)
+{
+ return extract32(desc->line_size_stride, 0, 18);
+}
+
+static uint32_t xlnx_dpdma_desc_get_line_stride(DPDMADescriptor *desc)
+{
+ return extract32(desc->line_size_stride, 18, 14) * 16;
+}
+
+static inline bool xlnx_dpdma_desc_crc_enabled(DPDMADescriptor *desc)
+{
+ return (desc->control & DSCR_CTRL_ENABLE_CRC) != 0;
+}
+
+static inline bool xlnx_dpdma_desc_check_crc(DPDMADescriptor *desc)
+{
+ uint32_t *p = (uint32_t *)desc;
+ uint32_t crc = 0;
+ uint8_t i;
+
+ /*
+ * CRC is calculated on the whole descriptor except the last 32bits word
+ * using 32bits addition.
+ */
+ for (i = 0; i < 15; i++) {
+ crc += p[i];
+ }
+
+ return crc == desc->crc;
+}
+
+static inline bool xlnx_dpdma_desc_completion_interrupt(DPDMADescriptor *desc)
+{
+ return (desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0;
+}
+
+static inline bool xlnx_dpdma_desc_is_valid(DPDMADescriptor *desc)
+{
+ return (desc->control & DSCR_CTRL_PREAMBLE) == CONTROL_PREAMBLE_VALUE;
+}
+
+static inline bool xlnx_dpdma_desc_is_contiguous(DPDMADescriptor *desc)
+{
+ return (desc->control & DSCR_CTRL_DESCRIPTOR_MODE) == 0;
+}
+
+static inline bool xlnx_dpdma_desc_update_enabled(DPDMADescriptor *desc)
+{
+ return (desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0;
+}
+
+static inline void xlnx_dpdma_desc_set_done(DPDMADescriptor *desc)
+{
+ desc->timestamp_msb |= STATUS_DONE;
+}
+
+static inline bool xlnx_dpdma_desc_is_already_done(DPDMADescriptor *desc)
+{
+ return (desc->timestamp_msb & STATUS_DONE) != 0;
+}
+
+static inline bool xlnx_dpdma_desc_ignore_done_bit(DPDMADescriptor *desc)
+{
+ return (desc->control & DSCR_CTRL_IGNORE_DONE) != 0;
+}
+
+static const VMStateDescription vmstate_xlnx_dpdma = {
+ .name = TYPE_XLNX_DPDMA,
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(registers, XlnxDPDMAState,
+ XLNX_DPDMA_REG_ARRAY_SIZE),
+ VMSTATE_BOOL_ARRAY(operation_finished, XlnxDPDMAState, 6),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void xlnx_dpdma_update_irq(XlnxDPDMAState *s)
+{
+ bool flags;
+
+ flags = ((s->registers[DPDMA_ISR] & (~s->registers[DPDMA_IMR]))
+ || (s->registers[DPDMA_EISR] & (~s->registers[DPDMA_EIMR])));
+ qemu_set_irq(s->irq, flags);
+}
+
+static uint64_t xlnx_dpdma_descriptor_start_address(XlnxDPDMAState *s,
+ uint8_t channel)
+{
+ return (s->registers[DPDMA_DSCR_STRT_ADDRE_CH(channel)] << 16)
+ + s->registers[DPDMA_DSCR_STRT_ADDR_CH(channel)];
+}
+
+static uint64_t xlnx_dpdma_descriptor_next_address(XlnxDPDMAState *s,
+ uint8_t channel)
+{
+ return ((uint64_t)s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] << 32)
+ + s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)];
+}
+
+static bool xlnx_dpdma_is_channel_enabled(XlnxDPDMAState *s,
+ uint8_t channel)
+{
+ return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_EN) != 0;
+}
+
+static bool xlnx_dpdma_is_channel_paused(XlnxDPDMAState *s,
+ uint8_t channel)
+{
+ return (s->registers[DPDMA_CNTL_CH(channel)] & DPDMA_CNTL_CH_PAUSED) != 0;
+}
+
+static inline bool xlnx_dpdma_is_channel_retriggered(XlnxDPDMAState *s,
+ uint8_t channel)
+{
+ /* Clear the retriggered bit after reading it. */
+ bool channel_is_retriggered = s->registers[DPDMA_GBL]
+ & DPDMA_GBL_RTRG_CH(channel);
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_RTRG_CH(channel);
+ return channel_is_retriggered;
+}
+
+static inline bool xlnx_dpdma_is_channel_triggered(XlnxDPDMAState *s,
+ uint8_t channel)
+{
+ return s->registers[DPDMA_GBL] & DPDMA_GBL_TRG_CH(channel);
+}
+
+static void xlnx_dpdma_update_desc_info(XlnxDPDMAState *s, uint8_t channel,
+ DPDMADescriptor *desc)
+{
+ s->registers[DPDMA_DSCR_NEXT_ADDRE_CH(channel)] =
+ extract32(desc->address_extension, 0, 16);
+ s->registers[DPDMA_DSCR_NEXT_ADDR_CH(channel)] = desc->next_descriptor;
+ s->registers[DPDMA_PYLD_CUR_ADDRE_CH(channel)] =
+ extract32(desc->address_extension, 16, 16);
+ s->registers[DPDMA_PYLD_CUR_ADDR_CH(channel)] = desc->source_address;
+ s->registers[DPDMA_VDO_CH(channel)] =
+ extract32(desc->line_size_stride, 18, 14)
+ + (extract32(desc->line_size_stride, 0, 18)
+ << 14);
+ s->registers[DPDMA_PYLD_SZ_CH(channel)] = desc->xfer_size;
+ s->registers[DPDMA_DSCR_ID_CH(channel)] = desc->descriptor_id;
+
+ /* Compute the status register with the descriptor information. */
+ s->registers[DPDMA_STATUS_CH(channel)] =
+ extract32(desc->control, 0, 8) << 13;
+ if ((desc->control & DSCR_CTRL_EN_DSCR_DONE_INTR) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_INTR;
+ }
+ if ((desc->control & DSCR_CTRL_EN_DSCR_UPDATE) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_DSCR_UP;
+ }
+ if ((desc->timestamp_msb & STATUS_DONE) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_DSCR_DONE;
+ }
+ if ((desc->control & DSCR_CTRL_IGNORE_DONE) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_IGNR_DONE;
+ }
+ if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR_OF_FRAME) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LDSCR_FRAME;
+ }
+ if ((desc->control & DSCR_CTRL_LAST_DESCRIPTOR) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_LAST_DSCR;
+ }
+ if ((desc->control & DSCR_CTRL_ENABLE_CRC) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_EN_CRC;
+ }
+ if ((desc->control & DSCR_CTRL_DESCRIPTOR_MODE) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_MODE;
+ }
+ if ((desc->control & DSCR_CTRL_AXI_BURST_TYPE) != 0) {
+ s->registers[DPDMA_STATUS_CH(channel)] |= DPDMA_STATUS_BURST_TYPE;
+ }
+}
+
+static void xlnx_dpdma_dump_descriptor(DPDMADescriptor *desc)
+{
+ if (DEBUG_DPDMA) {
+ qemu_log("DUMP DESCRIPTOR:\n");
+ qemu_hexdump((char *)desc, stdout, "", sizeof(DPDMADescriptor));
+ }
+}
+
+static uint64_t xlnx_dpdma_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ XlnxDPDMAState *s = XLNX_DPDMA(opaque);
+
+ DPRINTF("read @%" HWADDR_PRIx "\n", offset);
+ offset = offset >> 2;
+
+ switch (offset) {
+ /*
+ * Trying to read a write only register.
+ */
+ case DPDMA_GBL:
+ return 0;
+ default:
+ assert(offset <= (0xFFC >> 2));
+ return s->registers[offset];
+ }
+ return 0;
+}
+
+static void xlnx_dpdma_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ XlnxDPDMAState *s = XLNX_DPDMA(opaque);
+
+ DPRINTF("write @%" HWADDR_PRIx " = %" PRIx64 "\n", offset, value);
+ offset = offset >> 2;
+
+ switch (offset) {
+ case DPDMA_ISR:
+ s->registers[DPDMA_ISR] &= ~value;
+ xlnx_dpdma_update_irq(s);
+ break;
+ case DPDMA_IEN:
+ s->registers[DPDMA_IMR] &= ~value;
+ break;
+ case DPDMA_IDS:
+ s->registers[DPDMA_IMR] |= value;
+ break;
+ case DPDMA_EISR:
+ s->registers[DPDMA_EISR] &= ~value;
+ xlnx_dpdma_update_irq(s);
+ break;
+ case DPDMA_EIEN:
+ s->registers[DPDMA_EIMR] &= ~value;
+ break;
+ case DPDMA_EIDS:
+ s->registers[DPDMA_EIMR] |= value;
+ break;
+ case DPDMA_IMR:
+ case DPDMA_EIMR:
+ case DPDMA_DSCR_NEXT_ADDRE_CH(0):
+ case DPDMA_DSCR_NEXT_ADDRE_CH(1):
+ case DPDMA_DSCR_NEXT_ADDRE_CH(2):
+ case DPDMA_DSCR_NEXT_ADDRE_CH(3):
+ case DPDMA_DSCR_NEXT_ADDRE_CH(4):
+ case DPDMA_DSCR_NEXT_ADDRE_CH(5):
+ case DPDMA_DSCR_NEXT_ADDR_CH(0):
+ case DPDMA_DSCR_NEXT_ADDR_CH(1):
+ case DPDMA_DSCR_NEXT_ADDR_CH(2):
+ case DPDMA_DSCR_NEXT_ADDR_CH(3):
+ case DPDMA_DSCR_NEXT_ADDR_CH(4):
+ case DPDMA_DSCR_NEXT_ADDR_CH(5):
+ case DPDMA_PYLD_CUR_ADDRE_CH(0):
+ case DPDMA_PYLD_CUR_ADDRE_CH(1):
+ case DPDMA_PYLD_CUR_ADDRE_CH(2):
+ case DPDMA_PYLD_CUR_ADDRE_CH(3):
+ case DPDMA_PYLD_CUR_ADDRE_CH(4):
+ case DPDMA_PYLD_CUR_ADDRE_CH(5):
+ case DPDMA_PYLD_CUR_ADDR_CH(0):
+ case DPDMA_PYLD_CUR_ADDR_CH(1):
+ case DPDMA_PYLD_CUR_ADDR_CH(2):
+ case DPDMA_PYLD_CUR_ADDR_CH(3):
+ case DPDMA_PYLD_CUR_ADDR_CH(4):
+ case DPDMA_PYLD_CUR_ADDR_CH(5):
+ case DPDMA_STATUS_CH(0):
+ case DPDMA_STATUS_CH(1):
+ case DPDMA_STATUS_CH(2):
+ case DPDMA_STATUS_CH(3):
+ case DPDMA_STATUS_CH(4):
+ case DPDMA_STATUS_CH(5):
+ case DPDMA_VDO_CH(0):
+ case DPDMA_VDO_CH(1):
+ case DPDMA_VDO_CH(2):
+ case DPDMA_VDO_CH(3):
+ case DPDMA_VDO_CH(4):
+ case DPDMA_VDO_CH(5):
+ case DPDMA_PYLD_SZ_CH(0):
+ case DPDMA_PYLD_SZ_CH(1):
+ case DPDMA_PYLD_SZ_CH(2):
+ case DPDMA_PYLD_SZ_CH(3):
+ case DPDMA_PYLD_SZ_CH(4):
+ case DPDMA_PYLD_SZ_CH(5):
+ case DPDMA_DSCR_ID_CH(0):
+ case DPDMA_DSCR_ID_CH(1):
+ case DPDMA_DSCR_ID_CH(2):
+ case DPDMA_DSCR_ID_CH(3):
+ case DPDMA_DSCR_ID_CH(4):
+ case DPDMA_DSCR_ID_CH(5):
+ /*
+ * Trying to write to a read only register..
+ */
+ break;
+ case DPDMA_GBL:
+ /*
+ * This is a write only register so it's read as zero in the read
+ * callback.
+ * We store the value anyway so we can know if the channel is
+ * enabled.
+ */
+ s->registers[offset] |= value & 0x00000FFF;
+ break;
+ case DPDMA_DSCR_STRT_ADDRE_CH(0):
+ case DPDMA_DSCR_STRT_ADDRE_CH(1):
+ case DPDMA_DSCR_STRT_ADDRE_CH(2):
+ case DPDMA_DSCR_STRT_ADDRE_CH(3):
+ case DPDMA_DSCR_STRT_ADDRE_CH(4):
+ case DPDMA_DSCR_STRT_ADDRE_CH(5):
+ value &= 0x0000FFFF;
+ s->registers[offset] = value;
+ break;
+ case DPDMA_CNTL_CH(0):
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(0);
+ value &= 0x3FFFFFFF;
+ s->registers[offset] = value;
+ break;
+ case DPDMA_CNTL_CH(1):
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(1);
+ value &= 0x3FFFFFFF;
+ s->registers[offset] = value;
+ break;
+ case DPDMA_CNTL_CH(2):
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(2);
+ value &= 0x3FFFFFFF;
+ s->registers[offset] = value;
+ break;
+ case DPDMA_CNTL_CH(3):
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(3);
+ value &= 0x3FFFFFFF;
+ s->registers[offset] = value;
+ break;
+ case DPDMA_CNTL_CH(4):
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(4);
+ value &= 0x3FFFFFFF;
+ s->registers[offset] = value;
+ break;
+ case DPDMA_CNTL_CH(5):
+ s->registers[DPDMA_GBL] &= ~DPDMA_GBL_TRG_CH(5);
+ value &= 0x3FFFFFFF;
+ s->registers[offset] = value;
+ break;
+ default:
+ assert(offset <= (0xFFC >> 2));
+ s->registers[offset] = value;
+ break;
+ }
+}
+
+static const MemoryRegionOps dma_ops = {
+ .read = xlnx_dpdma_read,
+ .write = xlnx_dpdma_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void xlnx_dpdma_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ XlnxDPDMAState *s = XLNX_DPDMA(obj);
+
+ memory_region_init_io(&s->iomem, obj, &dma_ops, s,
+ TYPE_XLNX_DPDMA, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+
+static void xlnx_dpdma_reset(DeviceState *dev)
+{
+ XlnxDPDMAState *s = XLNX_DPDMA(dev);
+ size_t i;
+
+ memset(s->registers, 0, sizeof(s->registers));
+ s->registers[DPDMA_IMR] = 0x07FFFFFF;
+ s->registers[DPDMA_EIMR] = 0xFFFFFFFF;
+ s->registers[DPDMA_ALC0_MIN] = 0x0000FFFF;
+ s->registers[DPDMA_ALC1_MIN] = 0x0000FFFF;
+
+ for (i = 0; i < 6; i++) {
+ s->data[i] = NULL;
+ s->operation_finished[i] = true;
+ }
+}
+
+static void xlnx_dpdma_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->vmsd = &vmstate_xlnx_dpdma;
+ dc->reset = xlnx_dpdma_reset;
+}
+
+static const TypeInfo xlnx_dpdma_info = {
+ .name = TYPE_XLNX_DPDMA,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XlnxDPDMAState),
+ .instance_init = xlnx_dpdma_init,
+ .class_init = xlnx_dpdma_class_init,
+};
+
+static void xlnx_dpdma_register_types(void)
+{
+ type_register_static(&xlnx_dpdma_info);
+}
+
+size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel,
+ bool one_desc)
+{
+ uint64_t desc_addr;
+ uint64_t source_addr[6];
+ DPDMADescriptor desc;
+ bool done = false;
+ size_t ptr = 0;
+
+ assert(channel <= 5);
+
+ DPRINTF("start dpdma channel 0x%" PRIX8 "\n", channel);
+
+ if (!xlnx_dpdma_is_channel_triggered(s, channel)) {
+ DPRINTF("Channel isn't triggered..\n");
+ return 0;
+ }
+
+ if (!xlnx_dpdma_is_channel_enabled(s, channel)) {
+ DPRINTF("Channel isn't enabled..\n");
+ return 0;
+ }
+
+ if (xlnx_dpdma_is_channel_paused(s, channel)) {
+ DPRINTF("Channel is paused..\n");
+ return 0;
+ }
+
+ do {
+ if ((s->operation_finished[channel])
+ || xlnx_dpdma_is_channel_retriggered(s, channel)) {
+ desc_addr = xlnx_dpdma_descriptor_start_address(s, channel);
+ s->operation_finished[channel] = false;
+ } else {
+ desc_addr = xlnx_dpdma_descriptor_next_address(s, channel);
+ }
+
+ if (dma_memory_read(&address_space_memory, desc_addr, &desc,
+ sizeof(DPDMADescriptor))) {
+ s->registers[DPDMA_EISR] |= ((1 << 1) << channel);
+ xlnx_dpdma_update_irq(s);
+ s->operation_finished[channel] = true;
+ DPRINTF("Can't get the descriptor.\n");
+ break;
+ }
+
+ xlnx_dpdma_update_desc_info(s, channel, &desc);
+
+#ifdef DEBUG_DPDMA
+ xlnx_dpdma_dump_descriptor(&desc);
+#endif
+
+ DPRINTF("location of the descriptor: %" PRIx64 "\n", desc_addr);
+ if (!xlnx_dpdma_desc_is_valid(&desc)) {
+ s->registers[DPDMA_EISR] |= ((1 << 7) << channel);
+ xlnx_dpdma_update_irq(s);
+ s->operation_finished[channel] = true;
+ DPRINTF("Invalid descriptor..\n");
+ break;
+ }
+
+ if (xlnx_dpdma_desc_crc_enabled(&desc)
+ && !xlnx_dpdma_desc_check_crc(&desc)) {
+ s->registers[DPDMA_EISR] |= ((1 << 13) << channel);
+ xlnx_dpdma_update_irq(s);
+ s->operation_finished[channel] = true;
+ DPRINTF("Bad CRC for descriptor..\n");
+ break;
+ }
+
+ if (xlnx_dpdma_desc_is_already_done(&desc)
+ && !xlnx_dpdma_desc_ignore_done_bit(&desc)) {
+ /* We are trying to process an already processed descriptor. */
+ s->registers[DPDMA_EISR] |= ((1 << 25) << channel);
+ xlnx_dpdma_update_irq(s);
+ s->operation_finished[channel] = true;
+ DPRINTF("Already processed descriptor..\n");
+ break;
+ }
+
+ done = xlnx_dpdma_desc_is_last(&desc)
+ || xlnx_dpdma_desc_is_last_of_frame(&desc);
+
+ s->operation_finished[channel] = done;
+ if (s->data[channel]) {
+ int64_t transfer_len = xlnx_dpdma_desc_get_transfer_size(&desc);
+ uint32_t line_size = xlnx_dpdma_desc_get_line_size(&desc);
+ uint32_t line_stride = xlnx_dpdma_desc_get_line_stride(&desc);
+ if (xlnx_dpdma_desc_is_contiguous(&desc)) {
+ source_addr[0] = xlnx_dpdma_desc_get_source_address(&desc, 0);
+ while (transfer_len != 0) {
+ if (dma_memory_read(&address_space_memory,
+ source_addr[0],
+ &s->data[channel][ptr],
+ line_size)) {
+ s->registers[DPDMA_ISR] |= ((1 << 12) << channel);
+ xlnx_dpdma_update_irq(s);
+ DPRINTF("Can't get data.\n");
+ break;
+ }
+ ptr += line_size;
+ transfer_len -= line_size;
+ source_addr[0] += line_stride;
+ }
+ } else {
+ DPRINTF("Source address:\n");
+ int frag;
+ for (frag = 0; frag < 5; frag++) {
+ source_addr[frag] =
+ xlnx_dpdma_desc_get_source_address(&desc, frag);
+ DPRINTF("Fragment %u: %" PRIx64 "\n", frag + 1,
+ source_addr[frag]);
+ }
+
+ frag = 0;
+ while ((transfer_len < 0) && (frag < 5)) {
+ size_t fragment_len = DPDMA_FRAG_MAX_SZ
+ - (source_addr[frag] % DPDMA_FRAG_MAX_SZ);
+
+ if (dma_memory_read(&address_space_memory,
+ source_addr[frag],
+ &(s->data[channel][ptr]),
+ fragment_len)) {
+ s->registers[DPDMA_ISR] |= ((1 << 12) << channel);
+ xlnx_dpdma_update_irq(s);
+ DPRINTF("Can't get data.\n");
+ break;
+ }
+ ptr += fragment_len;
+ transfer_len -= fragment_len;
+ frag += 1;
+ }
+ }
+ }
+
+ if (xlnx_dpdma_desc_update_enabled(&desc)) {
+ /* The descriptor need to be updated when it's completed. */
+ DPRINTF("update the descriptor with the done flag set.\n");
+ xlnx_dpdma_desc_set_done(&desc);
+ dma_memory_write(&address_space_memory, desc_addr, &desc,
+ sizeof(DPDMADescriptor));
+ }
+
+ if (xlnx_dpdma_desc_completion_interrupt(&desc)) {
+ DPRINTF("completion interrupt enabled!\n");
+ s->registers[DPDMA_ISR] |= (1 << channel);
+ xlnx_dpdma_update_irq(s);
+ }
+
+ } while (!done && !one_desc);
+
+ return ptr;
+}
+
+void xlnx_dpdma_set_host_data_location(XlnxDPDMAState *s, uint8_t channel,
+ void *p)
+{
+ if (!s) {
+ qemu_log_mask(LOG_UNIMP, "DPDMA client not attached to valid DPDMA"
+ " instance\n");
+ return;
+ }
+
+ assert(channel <= 5);
+ s->data[channel] = p;
+}
+
+void xlnx_dpdma_trigger_vsync_irq(XlnxDPDMAState *s)
+{
+ s->registers[DPDMA_ISR] |= (1 << 27);
+ xlnx_dpdma_update_irq(s);
+}
+
+type_init(xlnx_dpdma_register_types)
diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c
index 9b1b004..dabef4a 100644
--- a/hw/gpio/omap_gpio.c
+++ b/hw/gpio/omap_gpio.c
@@ -23,6 +23,7 @@
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
struct omap_gpio_s {
qemu_irq irq;
@@ -678,48 +679,46 @@ static const MemoryRegionOps omap2_gpif_top_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int omap_gpio_init(SysBusDevice *sbd)
+static void omap_gpio_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- struct omap_gpif_s *s = OMAP1_GPIO(dev);
+ DeviceState *dev = DEVICE(obj);
+ struct omap_gpif_s *s = OMAP1_GPIO(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- if (!s->clk) {
- error_report("omap-gpio: clk not connected");
- return -1;
- }
qdev_init_gpio_in(dev, omap_gpio_set, 16);
qdev_init_gpio_out(dev, s->omap1.handler, 16);
sysbus_init_irq(sbd, &s->omap1.irq);
- memory_region_init_io(&s->iomem, OBJECT(s), &omap_gpio_ops, &s->omap1,
+ memory_region_init_io(&s->iomem, obj, &omap_gpio_ops, &s->omap1,
"omap.gpio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
- return 0;
}
-static int omap2_gpio_init(SysBusDevice *sbd)
+static void omap_gpio_realize(DeviceState *dev, Error **errp)
+{
+ struct omap_gpif_s *s = OMAP1_GPIO(dev);
+
+ if (!s->clk) {
+ error_setg(errp, "omap-gpio: clk not connected");
+ }
+}
+
+static void omap2_gpio_realize(DeviceState *dev, Error **errp)
{
- DeviceState *dev = DEVICE(sbd);
struct omap2_gpif_s *s = OMAP2_GPIO(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
int i;
if (!s->iclk) {
- error_report("omap2-gpio: iclk not connected");
- return -1;
+ error_setg(errp, "omap2-gpio: iclk not connected");
+ return;
}
s->modulecount = s->mpu_model < omap2430 ? 4
- : s->mpu_model < omap3430 ? 5
- : 6;
-
- for (i = 0; i < s->modulecount; i++) {
- if (!s->fclk[i]) {
- error_report("omap2-gpio: fclk%d not connected", i);
- return -1;
- }
- }
+ : s->mpu_model < omap3430 ? 5
+ : 6;
if (s->mpu_model < omap3430) {
- memory_region_init_io(&s->iomem, OBJECT(s), &omap2_gpif_top_ops, s,
+ memory_region_init_io(&s->iomem, OBJECT(dev), &omap2_gpif_top_ops, s,
"omap2.gpio", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
@@ -732,17 +731,20 @@ static int omap2_gpio_init(SysBusDevice *sbd)
for (i = 0; i < s->modulecount; i++) {
struct omap2_gpio_s *m = &s->modules[i];
+ if (!s->fclk[i]) {
+ error_setg(errp, "omap2-gpio: fclk%d not connected", i);
+ return;
+ }
+
m->revision = (s->mpu_model < omap3430) ? 0x18 : 0x25;
m->handler = &s->handler[i * 32];
sysbus_init_irq(sbd, &m->irq[0]); /* mpu irq */
sysbus_init_irq(sbd, &m->irq[1]); /* dsp irq */
sysbus_init_irq(sbd, &m->wkup);
- memory_region_init_io(&m->iomem, OBJECT(s), &omap2_gpio_module_ops, m,
+ memory_region_init_io(&m->iomem, OBJECT(dev), &omap2_gpio_module_ops, m,
"omap.gpio-module", 0x1000);
sysbus_init_mmio(sbd, &m->iomem);
}
-
- return 0;
}
/* Using qdev pointer properties for the clocks is not ideal.
@@ -766,9 +768,8 @@ static Property omap_gpio_properties[] = {
static void omap_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = omap_gpio_init;
+ dc->realize = omap_gpio_realize;
dc->reset = omap_gpif_reset;
dc->props = omap_gpio_properties;
/* Reason: pointer property "clk" */
@@ -779,6 +780,7 @@ static const TypeInfo omap_gpio_info = {
.name = TYPE_OMAP1_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct omap_gpif_s),
+ .instance_init = omap_gpio_init,
.class_init = omap_gpio_class_init,
};
@@ -797,9 +799,8 @@ static Property omap2_gpio_properties[] = {
static void omap2_gpio_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = omap2_gpio_init;
+ dc->realize = omap2_gpio_realize;
dc->reset = omap2_gpif_reset;
dc->props = omap2_gpio_properties;
/* Reason: pointer properties "iclk", "fclk0", ..., "fclk5" */
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
index 44faeb2..4ae2aa1 100644
--- a/hw/gpio/pl061.c
+++ b/hw/gpio/pl061.c
@@ -341,20 +341,6 @@ static const MemoryRegionOps pl061_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int pl061_initfn(SysBusDevice *sbd)
-{
- DeviceState *dev = DEVICE(sbd);
- PL061State *s = PL061(dev);
-
- memory_region_init_io(&s->iomem, OBJECT(s), &pl061_ops, s, "pl061", 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
- sysbus_init_irq(sbd, &s->irq);
- qdev_init_gpio_in(dev, pl061_set_irq, 8);
- qdev_init_gpio_out(dev, s->out, 8);
-
- return 0;
-}
-
static void pl061_luminary_init(Object *obj)
{
PL061State *s = PL061(obj);
@@ -366,17 +352,23 @@ static void pl061_luminary_init(Object *obj)
static void pl061_init(Object *obj)
{
PL061State *s = PL061(obj);
+ DeviceState *dev = DEVICE(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
s->id = pl061_id;
s->rsvd_start = 0x424;
+
+ memory_region_init_io(&s->iomem, obj, &pl061_ops, s, "pl061", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ qdev_init_gpio_in(dev, pl061_set_irq, 8);
+ qdev_init_gpio_out(dev, s->out, 8);
}
static void pl061_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = pl061_initfn;
dc->vmsd = &vmstate_pl061;
dc->reset = &pl061_reset;
}
diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c
index 555da28..15865e1 100644
--- a/hw/gpio/zaurus.c
+++ b/hw/gpio/zaurus.c
@@ -167,19 +167,18 @@ static void scoop_gpio_set(void *opaque, int line, int level)
s->gpio_level &= ~(1 << line);
}
-static int scoop_init(SysBusDevice *sbd)
+static void scoop_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- ScoopInfo *s = SCOOP(dev);
+ DeviceState *dev = DEVICE(obj);
+ ScoopInfo *s = SCOOP(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
s->status = 0x02;
qdev_init_gpio_out(dev, s->handler, 16);
qdev_init_gpio_in(dev, scoop_gpio_set, 16);
- memory_region_init_io(&s->iomem, OBJECT(s), &scoop_ops, s, "scoop", 0x1000);
+ memory_region_init_io(&s->iomem, obj, &scoop_ops, s, "scoop", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
-
- return 0;
}
static int scoop_post_load(void *opaque, int version_id)
@@ -239,9 +238,7 @@ static const VMStateDescription vmstate_scoop_regs = {
static void scoop_sysbus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = scoop_init;
dc->desc = "Scoop2 Sharp custom ASIC";
dc->vmsd = &vmstate_scoop_regs;
}
@@ -250,6 +247,7 @@ static const TypeInfo scoop_sysbus_info = {
.name = TYPE_SCOOP,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(ScoopInfo),
+ .instance_init = scoop_init,
.class_init = scoop_sysbus_class_init,
};
diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs
index 1fd54ed..a081b8e 100644
--- a/hw/i2c/Makefile.objs
+++ b/hw/i2c/Makefile.objs
@@ -1,4 +1,5 @@
common-obj-y += core.o smbus.o smbus_eeprom.o
+common-obj-$(CONFIG_DDC) += i2c-ddc.o
common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o
common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o
common-obj-$(CONFIG_APM) += pm_smbus.o
diff --git a/hw/i2c/bitbang_i2c.c b/hw/i2c/bitbang_i2c.c
index 6ed2060..d3a2989 100644
--- a/hw/i2c/bitbang_i2c.c
+++ b/hw/i2c/bitbang_i2c.c
@@ -210,13 +210,14 @@ static void bitbang_i2c_gpio_set(void *opaque, int irq, int level)
}
}
-static int gpio_i2c_init(SysBusDevice *sbd)
+static void gpio_i2c_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- GPIOI2CState *s = GPIO_I2C(dev);
+ DeviceState *dev = DEVICE(obj);
+ GPIOI2CState *s = GPIO_I2C(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
I2CBus *bus;
- memory_region_init(&s->dummy_iomem, OBJECT(s), "gpio_i2c", 0);
+ memory_region_init(&s->dummy_iomem, obj, "gpio_i2c", 0);
sysbus_init_mmio(sbd, &s->dummy_iomem);
bus = i2c_init_bus(dev, "i2c");
@@ -224,16 +225,12 @@ static int gpio_i2c_init(SysBusDevice *sbd)
qdev_init_gpio_in(dev, bitbang_i2c_gpio_set, 2);
qdev_init_gpio_out(dev, &s->out, 1);
-
- return 0;
}
static void gpio_i2c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = gpio_i2c_init;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
dc->desc = "Virtual GPIO to I2C bridge";
}
@@ -242,6 +239,7 @@ static const TypeInfo gpio_i2c_info = {
.name = TYPE_GPIO_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(GPIOI2CState),
+ .instance_init = gpio_i2c_init,
.class_init = gpio_i2c_class_init,
};
diff --git a/hw/i2c/core.c b/hw/i2c/core.c
index ba22104..abb3efb 100644
--- a/hw/i2c/core.c
+++ b/hw/i2c/core.c
@@ -10,12 +10,19 @@
#include "qemu/osdep.h"
#include "hw/i2c/i2c.h"
+typedef struct I2CNode I2CNode;
+
+struct I2CNode {
+ I2CSlave *elt;
+ QLIST_ENTRY(I2CNode) next;
+};
+
struct I2CBus
{
BusState qbus;
- I2CSlave *current_dev;
- I2CSlave *dev;
+ QLIST_HEAD(, I2CNode) current_devs;
uint8_t saved_address;
+ bool broadcast;
};
static Property i2c_props[] = {
@@ -36,17 +43,12 @@ static void i2c_bus_pre_save(void *opaque)
{
I2CBus *bus = opaque;
- bus->saved_address = bus->current_dev ? bus->current_dev->address : -1;
-}
-
-static int i2c_bus_post_load(void *opaque, int version_id)
-{
- I2CBus *bus = opaque;
-
- /* The bus is loaded before attached devices, so load and save the
- current device id. Devices will check themselves as loaded. */
- bus->current_dev = NULL;
- return 0;
+ bus->saved_address = -1;
+ if (!QLIST_EMPTY(&bus->current_devs)) {
+ if (!bus->broadcast) {
+ bus->saved_address = QLIST_FIRST(&bus->current_devs)->elt->address;
+ }
+ }
}
static const VMStateDescription vmstate_i2c_bus = {
@@ -54,9 +56,9 @@ static const VMStateDescription vmstate_i2c_bus = {
.version_id = 1,
.minimum_version_id = 1,
.pre_save = i2c_bus_pre_save,
- .post_load = i2c_bus_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT8(saved_address, I2CBus),
+ VMSTATE_BOOL(broadcast, I2CBus),
VMSTATE_END_OF_LIST()
}
};
@@ -67,6 +69,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name)
I2CBus *bus;
bus = I2C_BUS(qbus_create(TYPE_I2C_BUS, parent, name));
+ QLIST_INIT(&bus->current_devs);
vmstate_register(NULL, -1, &vmstate_i2c_bus, bus);
return bus;
}
@@ -79,7 +82,7 @@ void i2c_set_slave_address(I2CSlave *dev, uint8_t address)
/* Return nonzero if bus is busy. */
int i2c_bus_busy(I2CBus *bus)
{
- return bus->current_dev != NULL;
+ return !QLIST_EMPTY(&bus->current_devs);
}
/* Returns non-zero if the address is not valid. */
@@ -87,95 +90,127 @@ int i2c_bus_busy(I2CBus *bus)
int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv)
{
BusChild *kid;
- I2CSlave *slave = NULL;
I2CSlaveClass *sc;
+ I2CNode *node;
+
+ if (address == 0x00) {
+ /*
+ * This is a broadcast, the current_devs will be all the devices of the
+ * bus.
+ */
+ bus->broadcast = true;
+ }
QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
DeviceState *qdev = kid->child;
I2CSlave *candidate = I2C_SLAVE(qdev);
- if (candidate->address == address) {
- slave = candidate;
- break;
+ if ((candidate->address == address) || (bus->broadcast)) {
+ node = g_malloc(sizeof(struct I2CNode));
+ node->elt = candidate;
+ QLIST_INSERT_HEAD(&bus->current_devs, node, next);
+ if (!bus->broadcast) {
+ break;
+ }
}
}
- if (!slave) {
+ if (QLIST_EMPTY(&bus->current_devs)) {
return 1;
}
- sc = I2C_SLAVE_GET_CLASS(slave);
- /* If the bus is already busy, assume this is a repeated
- start condition. */
- bus->current_dev = slave;
- if (sc->event) {
- sc->event(slave, recv ? I2C_START_RECV : I2C_START_SEND);
+ QLIST_FOREACH(node, &bus->current_devs, next) {
+ sc = I2C_SLAVE_GET_CLASS(node->elt);
+ /* If the bus is already busy, assume this is a repeated
+ start condition. */
+ if (sc->event) {
+ sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND);
+ }
}
return 0;
}
void i2c_end_transfer(I2CBus *bus)
{
- I2CSlave *dev = bus->current_dev;
I2CSlaveClass *sc;
+ I2CNode *node, *next;
- if (!dev) {
+ if (QLIST_EMPTY(&bus->current_devs)) {
return;
}
- sc = I2C_SLAVE_GET_CLASS(dev);
- if (sc->event) {
- sc->event(dev, I2C_FINISH);
+ QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) {
+ sc = I2C_SLAVE_GET_CLASS(node->elt);
+ if (sc->event) {
+ sc->event(node->elt, I2C_FINISH);
+ }
+ QLIST_REMOVE(node, next);
+ g_free(node);
}
-
- bus->current_dev = NULL;
+ bus->broadcast = false;
}
-int i2c_send(I2CBus *bus, uint8_t data)
+int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send)
{
- I2CSlave *dev = bus->current_dev;
I2CSlaveClass *sc;
+ I2CNode *node;
+ int ret = 0;
+
+ if (send) {
+ QLIST_FOREACH(node, &bus->current_devs, next) {
+ sc = I2C_SLAVE_GET_CLASS(node->elt);
+ if (sc->send) {
+ ret = ret || sc->send(node->elt, *data);
+ } else {
+ ret = -1;
+ }
+ }
+ return ret ? -1 : 0;
+ } else {
+ if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) {
+ return -1;
+ }
- if (!dev) {
+ sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
+ if (sc->recv) {
+ ret = sc->recv(QLIST_FIRST(&bus->current_devs)->elt);
+ if (ret < 0) {
+ return ret;
+ } else {
+ *data = ret;
+ return 0;
+ }
+ }
return -1;
}
+}
- sc = I2C_SLAVE_GET_CLASS(dev);
- if (sc->send) {
- return sc->send(dev, data);
- }
-
- return -1;
+int i2c_send(I2CBus *bus, uint8_t data)
+{
+ return i2c_send_recv(bus, &data, true);
}
int i2c_recv(I2CBus *bus)
{
- I2CSlave *dev = bus->current_dev;
- I2CSlaveClass *sc;
-
- if (!dev) {
- return -1;
- }
+ uint8_t data;
+ int ret = i2c_send_recv(bus, &data, false);
- sc = I2C_SLAVE_GET_CLASS(dev);
- if (sc->recv) {
- return sc->recv(dev);
- }
-
- return -1;
+ return ret < 0 ? ret : data;
}
void i2c_nack(I2CBus *bus)
{
- I2CSlave *dev = bus->current_dev;
I2CSlaveClass *sc;
+ I2CNode *node;
- if (!dev) {
+ if (QLIST_EMPTY(&bus->current_devs)) {
return;
}
- sc = I2C_SLAVE_GET_CLASS(dev);
- if (sc->event) {
- sc->event(dev, I2C_NACK);
+ QLIST_FOREACH(node, &bus->current_devs, next) {
+ sc = I2C_SLAVE_GET_CLASS(node->elt);
+ if (sc->event) {
+ sc->event(node->elt, I2C_NACK);
+ }
}
}
@@ -183,9 +218,13 @@ static int i2c_slave_post_load(void *opaque, int version_id)
{
I2CSlave *dev = opaque;
I2CBus *bus;
+ I2CNode *node;
+
bus = I2C_BUS(qdev_get_parent_bus(DEVICE(dev)));
- if (bus->saved_address == dev->address) {
- bus->current_dev = dev;
+ if ((bus->saved_address == dev->address) || (bus->broadcast)) {
+ node = g_malloc(sizeof(struct I2CNode));
+ node->elt = dev;
+ QLIST_INSERT_HEAD(&bus->current_devs, node, next);
}
return 0;
}
diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c
index 8c2a2c1..c96fa7d 100644
--- a/hw/i2c/exynos4210_i2c.c
+++ b/hw/i2c/exynos4210_i2c.c
@@ -299,33 +299,32 @@ static void exynos4210_i2c_reset(DeviceState *d)
s->scl_free = true;
}
-static int exynos4210_i2c_realize(SysBusDevice *sbd)
+static void exynos4210_i2c_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- Exynos4210I2CState *s = EXYNOS4_I2C(dev);
+ DeviceState *dev = DEVICE(obj);
+ Exynos4210I2CState *s = EXYNOS4_I2C(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_i2c_ops, s,
+ memory_region_init_io(&s->iomem, obj, &exynos4210_i2c_ops, s,
TYPE_EXYNOS4_I2C, EXYNOS4_I2C_MEM_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
s->bus = i2c_init_bus(dev, "i2c");
- return 0;
}
static void exynos4210_i2c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass);
dc->vmsd = &exynos4210_i2c_vmstate;
dc->reset = exynos4210_i2c_reset;
- sbdc->init = exynos4210_i2c_realize;
}
static const TypeInfo exynos4210_i2c_type_info = {
.name = TYPE_EXYNOS4_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210I2CState),
+ .instance_init = exynos4210_i2c_init,
.class_init = exynos4210_i2c_class_init,
};
diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c
new file mode 100644
index 0000000..1227212
--- /dev/null
+++ b/hw/i2c/i2c-ddc.c
@@ -0,0 +1,308 @@
+/* A simple I2C slave for returning monitor EDID data via DDC.
+ *
+ * Copyright (c) 2011 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/i2c-ddc.h"
+
+#ifndef DEBUG_I2CDDC
+#define DEBUG_I2CDDC 0
+#endif
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_I2CDDC) { \
+ qemu_log("i2c-ddc: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0);
+
+/* Structure defining a monitor's characteristics in a
+ * readable format: this should be passed to build_edid_blob()
+ * to convert it into the 128 byte binary EDID blob.
+ * Not all bits of the EDID are customisable here.
+ */
+struct EDIDData {
+ char manuf_id[3]; /* three upper case letters */
+ uint16_t product_id;
+ uint32_t serial_no;
+ uint8_t manuf_week;
+ int manuf_year;
+ uint8_t h_cm;
+ uint8_t v_cm;
+ uint8_t gamma;
+ char monitor_name[14];
+ char serial_no_string[14];
+ /* Range limits */
+ uint8_t vmin; /* Hz */
+ uint8_t vmax; /* Hz */
+ uint8_t hmin; /* kHz */
+ uint8_t hmax; /* kHz */
+ uint8_t pixclock; /* MHz / 10 */
+ uint8_t timing_data[18];
+};
+
+typedef struct EDIDData EDIDData;
+
+/* EDID data for a simple LCD monitor */
+static const EDIDData lcd_edid = {
+ /* The manuf_id ought really to be an assigned EISA ID */
+ .manuf_id = "QMU",
+ .product_id = 0,
+ .serial_no = 1,
+ .manuf_week = 1,
+ .manuf_year = 2011,
+ .h_cm = 40,
+ .v_cm = 30,
+ .gamma = 0x78,
+ .monitor_name = "QEMU monitor",
+ .serial_no_string = "1",
+ .vmin = 40,
+ .vmax = 120,
+ .hmin = 30,
+ .hmax = 100,
+ .pixclock = 18,
+ .timing_data = {
+ /* Borrowed from a 21" LCD */
+ 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40,
+ 0xc0, 0x13, 0x00, 0x98, 0x32, 0x11, 0x00, 0x00, 0x1e
+ }
+};
+
+static uint8_t manuf_char_to_int(char c)
+{
+ return (c - 'A') & 0x1f;
+}
+
+static void write_ascii_descriptor_block(uint8_t *descblob, uint8_t blocktype,
+ const char *string)
+{
+ /* Write an EDID Descriptor Block of the "ascii string" type */
+ int i;
+ descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0;
+ descblob[3] = blocktype;
+ /* The rest is 13 bytes of ASCII; if less then the rest must
+ * be filled with newline then spaces
+ */
+ for (i = 5; i < 19; i++) {
+ descblob[i] = string[i - 5];
+ if (!descblob[i]) {
+ break;
+ }
+ }
+ if (i < 19) {
+ descblob[i++] = '\n';
+ }
+ for ( ; i < 19; i++) {
+ descblob[i] = ' ';
+ }
+}
+
+static void write_range_limits_descriptor(const EDIDData *edid,
+ uint8_t *descblob)
+{
+ int i;
+ descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0;
+ descblob[3] = 0xfd;
+ descblob[5] = edid->vmin;
+ descblob[6] = edid->vmax;
+ descblob[7] = edid->hmin;
+ descblob[8] = edid->hmax;
+ descblob[9] = edid->pixclock;
+ descblob[10] = 0;
+ descblob[11] = 0xa;
+ for (i = 12; i < 19; i++) {
+ descblob[i] = 0x20;
+ }
+}
+
+static void build_edid_blob(const EDIDData *edid, uint8_t *blob)
+{
+ /* Write an EDID 1.3 format blob (128 bytes) based
+ * on the EDIDData structure.
+ */
+ int i;
+ uint8_t cksum;
+
+ /* 00-07 : header */
+ blob[0] = blob[7] = 0;
+ for (i = 1 ; i < 7; i++) {
+ blob[i] = 0xff;
+ }
+ /* 08-09 : manufacturer ID */
+ blob[8] = (manuf_char_to_int(edid->manuf_id[0]) << 2)
+ | (manuf_char_to_int(edid->manuf_id[1]) >> 3);
+ blob[9] = (manuf_char_to_int(edid->manuf_id[1]) << 5)
+ | manuf_char_to_int(edid->manuf_id[2]);
+ /* 10-11 : product ID code */
+ blob[10] = edid->product_id;
+ blob[11] = edid->product_id >> 8;
+ blob[12] = edid->serial_no;
+ blob[13] = edid->serial_no >> 8;
+ blob[14] = edid->serial_no >> 16;
+ blob[15] = edid->serial_no >> 24;
+ /* 16 : week of manufacture */
+ blob[16] = edid->manuf_week;
+ /* 17 : year of manufacture - 1990 */
+ blob[17] = edid->manuf_year - 1990;
+ /* 18, 19 : EDID version and revision */
+ blob[18] = 1;
+ blob[19] = 3;
+ /* 20 - 24 : basic display parameters */
+ /* We are always a digital display */
+ blob[20] = 0x80;
+ /* 21, 22 : max h/v size in cm */
+ blob[21] = edid->h_cm;
+ blob[22] = edid->v_cm;
+ /* 23 : gamma (divide by 100 then add 1 for actual value) */
+ blob[23] = edid->gamma;
+ /* 24 feature support: no power management, RGB, preferred timing mode,
+ * standard colour space
+ */
+ blob[24] = 0x0e;
+ /* 25 - 34 : chromaticity coordinates. These are the
+ * standard sRGB chromaticity values
+ */
+ blob[25] = 0xee;
+ blob[26] = 0x91;
+ blob[27] = 0xa3;
+ blob[28] = 0x54;
+ blob[29] = 0x4c;
+ blob[30] = 0x99;
+ blob[31] = 0x26;
+ blob[32] = 0x0f;
+ blob[33] = 0x50;
+ blob[34] = 0x54;
+ /* 35, 36 : Established timings: claim to support everything */
+ blob[35] = blob[36] = 0xff;
+ /* 37 : manufacturer's reserved timing: none */
+ blob[37] = 0;
+ /* 38 - 53 : standard timing identification
+ * don't claim anything beyond what the 'established timings'
+ * already provide. Unused slots must be (0x1, 0x1)
+ */
+ for (i = 38; i < 54; i++) {
+ blob[i] = 0x1;
+ }
+ /* 54 - 71 : descriptor block 1 : must be preferred timing data */
+ memcpy(blob + 54, edid->timing_data, 18);
+ /* 72 - 89, 90 - 107, 108 - 125 : descriptor block 2, 3, 4
+ * Order not important, but we must have a monitor name and a
+ * range limits descriptor.
+ */
+ write_range_limits_descriptor(edid, blob + 72);
+ write_ascii_descriptor_block(blob + 90, 0xfc, edid->monitor_name);
+ write_ascii_descriptor_block(blob + 108, 0xff, edid->serial_no_string);
+
+ /* 126 : extension flag */
+ blob[126] = 0;
+
+ cksum = 0;
+ for (i = 0; i < 127; i++) {
+ cksum += blob[i];
+ }
+ /* 127 : checksum */
+ blob[127] = -cksum;
+ if (DEBUG_I2CDDC) {
+ qemu_hexdump((char *)blob, stdout, "", 128);
+ }
+}
+
+static void i2c_ddc_reset(DeviceState *ds)
+{
+ I2CDDCState *s = I2CDDC(ds);
+
+ s->firstbyte = false;
+ s->reg = 0;
+}
+
+static void i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
+{
+ I2CDDCState *s = I2CDDC(i2c);
+
+ if (event == I2C_START_SEND) {
+ s->firstbyte = true;
+ }
+}
+
+static int i2c_ddc_rx(I2CSlave *i2c)
+{
+ I2CDDCState *s = I2CDDC(i2c);
+
+ int value;
+ value = s->edid_blob[s->reg];
+ s->reg++;
+ return value;
+}
+
+static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data)
+{
+ I2CDDCState *s = I2CDDC(i2c);
+ if (s->firstbyte) {
+ s->reg = data;
+ s->firstbyte = false;
+ DPRINTF("[EDID] Written new pointer: %u\n", data);
+ return 1;
+ }
+
+ /* Ignore all writes */
+ s->reg++;
+ return 1;
+}
+
+static void i2c_ddc_init(Object *obj)
+{
+ I2CDDCState *s = I2CDDC(obj);
+ build_edid_blob(&lcd_edid, s->edid_blob);
+}
+
+static const VMStateDescription vmstate_i2c_ddc = {
+ .name = TYPE_I2CDDC,
+ .version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(firstbyte, I2CDDCState),
+ VMSTATE_UINT8(reg, I2CDDCState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void i2c_ddc_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc);
+
+ dc->reset = i2c_ddc_reset;
+ dc->vmsd = &vmstate_i2c_ddc;
+ isc->event = i2c_ddc_event;
+ isc->recv = i2c_ddc_rx;
+ isc->send = i2c_ddc_tx;
+}
+
+static TypeInfo i2c_ddc_info = {
+ .name = TYPE_I2CDDC,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(I2CDDCState),
+ .instance_init = i2c_ddc_init,
+ .class_init = i2c_ddc_class_init
+};
+
+static void ddc_register_devices(void)
+{
+ type_register_static(&i2c_ddc_info);
+}
+
+type_init(ddc_register_devices);
diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c
index 67fbbff..f7c92ea 100644
--- a/hw/i2c/omap_i2c.c
+++ b/hw/i2c/omap_i2c.c
@@ -22,6 +22,7 @@
#include "hw/arm/omap.h"
#include "hw/sysbus.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
#define TYPE_OMAP_I2C "omap_i2c"
#define OMAP_I2C(obj) OBJECT_CHECK(OMAPI2CState, (obj), TYPE_OMAP_I2C)
@@ -445,29 +446,35 @@ static const MemoryRegionOps omap_i2c_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int omap_i2c_init(SysBusDevice *sbd)
+static void omap_i2c_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ OMAPI2CState *s = OMAP_I2C(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ sysbus_init_irq(sbd, &s->irq);
+ sysbus_init_irq(sbd, &s->drq[0]);
+ sysbus_init_irq(sbd, &s->drq[1]);
+ sysbus_init_mmio(sbd, &s->iomem);
+ s->bus = i2c_init_bus(dev, NULL);
+}
+
+static void omap_i2c_realize(DeviceState *dev, Error **errp)
{
- DeviceState *dev = DEVICE(sbd);
OMAPI2CState *s = OMAP_I2C(dev);
+ memory_region_init_io(&s->iomem, OBJECT(dev), &omap_i2c_ops, s, "omap.i2c",
+ (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000);
+
if (!s->fclk) {
- error_report("omap_i2c: fclk not connected");
- return -1;
+ error_setg(errp, "omap_i2c: fclk not connected");
+ return;
}
if (s->revision >= OMAP2_INTR_REV && !s->iclk) {
/* Note that OMAP1 doesn't have a separate interface clock */
- error_report("omap_i2c: iclk not connected");
- return -1;
+ error_setg(errp, "omap_i2c: iclk not connected");
+ return;
}
-
- sysbus_init_irq(sbd, &s->irq);
- sysbus_init_irq(sbd, &s->drq[0]);
- sysbus_init_irq(sbd, &s->drq[1]);
- memory_region_init_io(&s->iomem, OBJECT(s), &omap_i2c_ops, s, "omap.i2c",
- (s->revision < OMAP2_INTR_REV) ? 0x800 : 0x1000);
- sysbus_init_mmio(sbd, &s->iomem);
- s->bus = i2c_init_bus(dev, NULL);
- return 0;
}
static Property omap_i2c_properties[] = {
@@ -480,18 +487,19 @@ static Property omap_i2c_properties[] = {
static void omap_i2c_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = omap_i2c_init;
+
dc->props = omap_i2c_properties;
dc->reset = omap_i2c_reset;
/* Reason: pointer properties "iclk", "fclk" */
dc->cannot_instantiate_with_device_add_yet = true;
+ dc->realize = omap_i2c_realize;
}
static const TypeInfo omap_i2c_info = {
.name = TYPE_OMAP_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(OMAPI2CState),
+ .instance_init = omap_i2c_init,
.class_init = omap_i2c_class_init,
};
diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c
index 0bce524..da9f298 100644
--- a/hw/i2c/versatile_i2c.c
+++ b/hw/i2c/versatile_i2c.c
@@ -79,32 +79,25 @@ static const MemoryRegionOps versatile_i2c_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int versatile_i2c_init(SysBusDevice *sbd)
+static void versatile_i2c_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- VersatileI2CState *s = VERSATILE_I2C(dev);
+ DeviceState *dev = DEVICE(obj);
+ VersatileI2CState *s = VERSATILE_I2C(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
I2CBus *bus;
bus = i2c_init_bus(dev, "i2c");
s->bitbang = bitbang_i2c_init(bus);
- memory_region_init_io(&s->iomem, OBJECT(s), &versatile_i2c_ops, s,
+ memory_region_init_io(&s->iomem, obj, &versatile_i2c_ops, s,
"versatile_i2c", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
- return 0;
-}
-
-static void versatile_i2c_class_init(ObjectClass *klass, void *data)
-{
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
-
- k->init = versatile_i2c_init;
}
static const TypeInfo versatile_i2c_info = {
.name = TYPE_VERSATILE_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(VersatileI2CState),
- .class_init = versatile_i2c_class_init,
+ .instance_init = versatile_i2c_init,
};
static void versatile_i2c_register_types(void)
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index bc0dd2c..ffb49c1 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -51,3 +51,4 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_EDU) += edu.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
+obj-$(CONFIG_AUX) += aux.o
diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c
index 4442227..66a0787 100644
--- a/hw/misc/arm_l2x0.c
+++ b/hw/misc/arm_l2x0.c
@@ -159,14 +159,14 @@ static const MemoryRegionOps l2x0_mem_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static int l2x0_priv_init(SysBusDevice *dev)
+static void l2x0_priv_init(Object *obj)
{
- L2x0State *s = ARM_L2X0(dev);
+ L2x0State *s = ARM_L2X0(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
- memory_region_init_io(&s->iomem, OBJECT(dev), &l2x0_mem_ops, s,
+ memory_region_init_io(&s->iomem, obj, &l2x0_mem_ops, s,
"l2x0_cc", 0x1000);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static Property l2x0_properties[] = {
@@ -176,10 +176,8 @@ static Property l2x0_properties[] = {
static void l2x0_class_init(ObjectClass *klass, void *data)
{
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
- k->init = l2x0_priv_init;
dc->vmsd = &vmstate_l2x0;
dc->props = l2x0_properties;
dc->reset = l2x0_priv_reset;
@@ -189,6 +187,7 @@ static const TypeInfo l2x0_info = {
.name = TYPE_ARM_L2X0,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(L2x0State),
+ .instance_init = l2x0_priv_init,
.class_init = l2x0_class_init,
};
diff --git a/hw/misc/aux.c b/hw/misc/aux.c
new file mode 100644
index 0000000..25d7712
--- /dev/null
+++ b/hw/misc/aux.c
@@ -0,0 +1,292 @@
+/*
+ * aux.c
+ *
+ * Copyright 2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This is an implementation of the AUX bus for VESA Display Port v1.1a.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/misc/aux.h"
+#include "hw/i2c/i2c.h"
+#include "monitor/monitor.h"
+
+#ifndef DEBUG_AUX
+#define DEBUG_AUX 0
+#endif
+
+#define DPRINTF(fmt, ...) do { \
+ if (DEBUG_AUX) { \
+ qemu_log("aux: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0);
+
+#define TYPE_AUXTOI2C "aux-to-i2c-bridge"
+#define AUXTOI2C(obj) OBJECT_CHECK(AUXTOI2CState, (obj), TYPE_AUXTOI2C)
+
+static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent);
+static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge);
+
+/* aux-bus implementation (internal not public) */
+static void aux_bus_class_init(ObjectClass *klass, void *data)
+{
+ BusClass *k = BUS_CLASS(klass);
+
+ /* AUXSlave has an MMIO so we need to change the way we print information
+ * in monitor.
+ */
+ k->print_dev = aux_slave_dev_print;
+}
+
+AUXBus *aux_init_bus(DeviceState *parent, const char *name)
+{
+ AUXBus *bus;
+
+ bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name));
+ bus->bridge = AUXTOI2C(qdev_create(BUS(bus), TYPE_AUXTOI2C));
+
+ /* Memory related. */
+ bus->aux_io = g_malloc(sizeof(*bus->aux_io));
+ memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", (1 << 20));
+ address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io");
+ return bus;
+}
+
+static void aux_bus_map_device(AUXBus *bus, AUXSlave *dev, hwaddr addr)
+{
+ memory_region_add_subregion(bus->aux_io, addr, dev->mmio);
+}
+
+static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev)
+{
+ return (dev == DEVICE(bus->bridge));
+}
+
+I2CBus *aux_get_i2c_bus(AUXBus *bus)
+{
+ return aux_bridge_get_i2c_bus(bus->bridge);
+}
+
+AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
+ uint8_t len, uint8_t *data)
+{
+ AUXReply ret = AUX_NACK;
+ I2CBus *i2c_bus = aux_get_i2c_bus(bus);
+ size_t i;
+ bool is_write = false;
+
+ DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address,
+ cmd, len);
+
+ switch (cmd) {
+ /*
+ * Forward the request on the AUX bus..
+ */
+ case WRITE_AUX:
+ case READ_AUX:
+ is_write = cmd == READ_AUX ? false : true;
+ for (i = 0; i < len; i++) {
+ if (!address_space_rw(&bus->aux_addr_space, address++,
+ MEMTXATTRS_UNSPECIFIED, data++, 1,
+ is_write)) {
+ ret = AUX_I2C_ACK;
+ } else {
+ ret = AUX_NACK;
+ break;
+ }
+ }
+ break;
+ /*
+ * Classic I2C transactions..
+ */
+ case READ_I2C:
+ case WRITE_I2C:
+ is_write = cmd == READ_I2C ? false : true;
+ if (i2c_bus_busy(i2c_bus)) {
+ i2c_end_transfer(i2c_bus);
+ }
+
+ if (i2c_start_transfer(i2c_bus, address, is_write)) {
+ ret = AUX_I2C_NACK;
+ break;
+ }
+
+ ret = AUX_I2C_ACK;
+ while (len > 0) {
+ if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
+ ret = AUX_I2C_NACK;
+ break;
+ }
+ len--;
+ }
+ i2c_end_transfer(i2c_bus);
+ break;
+ /*
+ * I2C MOT transactions.
+ *
+ * Here we send a start when:
+ * - We didn't start transaction yet.
+ * - We had a READ and we do a WRITE.
+ * - We changed the address.
+ */
+ case WRITE_I2C_MOT:
+ case READ_I2C_MOT:
+ is_write = cmd == READ_I2C_MOT ? false : true;
+ if (!i2c_bus_busy(i2c_bus)) {
+ /*
+ * No transactions started..
+ */
+ if (i2c_start_transfer(i2c_bus, address, is_write)) {
+ ret = AUX_I2C_NACK;
+ break;
+ }
+ } else if ((address != bus->last_i2c_address) ||
+ (bus->last_transaction != cmd)) {
+ /*
+ * Transaction started but we need to restart..
+ */
+ i2c_end_transfer(i2c_bus);
+ if (i2c_start_transfer(i2c_bus, address, is_write)) {
+ ret = AUX_I2C_NACK;
+ break;
+ }
+ }
+
+ while (len > 0) {
+ if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
+ ret = AUX_I2C_NACK;
+ i2c_end_transfer(i2c_bus);
+ break;
+ }
+ len--;
+ }
+ bus->last_transaction = cmd;
+ bus->last_i2c_address = address;
+ ret = AUX_I2C_ACK;
+ break;
+ default:
+ DPRINTF("Not implemented!\n");
+ return AUX_NACK;
+ }
+
+ DPRINTF("reply: %u\n", ret);
+ return ret;
+}
+
+static const TypeInfo aux_bus_info = {
+ .name = TYPE_AUX_BUS,
+ .parent = TYPE_BUS,
+ .instance_size = sizeof(AUXBus),
+ .class_init = aux_bus_class_init
+};
+
+/* aux-i2c implementation (internal not public) */
+struct AUXTOI2CState {
+ /*< private >*/
+ DeviceState parent_obj;
+
+ /*< public >*/
+ I2CBus *i2c_bus;
+};
+
+static void aux_bridge_init(Object *obj)
+{
+ AUXTOI2CState *s = AUXTOI2C(obj);
+
+ s->i2c_bus = i2c_init_bus(DEVICE(obj), "aux-i2c");
+}
+
+static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge)
+{
+ return bridge->i2c_bus;
+}
+
+static const TypeInfo aux_to_i2c_type_info = {
+ .name = TYPE_AUXTOI2C,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(AUXTOI2CState),
+ .instance_init = aux_bridge_init
+};
+
+/* aux-slave implementation */
+static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+ AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
+ AUXSlave *s;
+
+ /* Don't print anything if the device is I2C "bridge". */
+ if (aux_bus_is_bridge(bus, dev)) {
+ return;
+ }
+
+ s = AUX_SLAVE(dev);
+
+ monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
+ indent, "",
+ object_property_get_int(OBJECT(s->mmio), "addr", NULL),
+ memory_region_size(s->mmio));
+}
+
+DeviceState *aux_create_slave(AUXBus *bus, const char *type, uint32_t addr)
+{
+ DeviceState *dev;
+
+ dev = DEVICE(object_new(type));
+ assert(dev);
+ qdev_set_parent_bus(dev, &bus->qbus);
+ qdev_init_nofail(dev);
+ aux_bus_map_device(AUX_BUS(qdev_get_parent_bus(dev)), AUX_SLAVE(dev), addr);
+ return dev;
+}
+
+void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio)
+{
+ assert(!aux_slave->mmio);
+ aux_slave->mmio = mmio;
+}
+
+static void aux_slave_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *k = DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_MISC, k->categories);
+ k->bus_type = TYPE_AUX_BUS;
+}
+
+static const TypeInfo aux_slave_type_info = {
+ .name = TYPE_AUX_SLAVE,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(AUXSlave),
+ .abstract = true,
+ .class_init = aux_slave_class_init,
+};
+
+static void aux_register_types(void)
+{
+ type_register_static(&aux_bus_info);
+ type_register_static(&aux_slave_type_info);
+ type_register_static(&aux_to_i2c_type_info);
+}
+
+type_init(aux_register_types)
diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c
index 889abad..e30dbc7 100644
--- a/hw/misc/exynos4210_pmu.c
+++ b/hw/misc/exynos4210_pmu.c
@@ -457,15 +457,15 @@ static void exynos4210_pmu_reset(DeviceState *dev)
}
}
-static int exynos4210_pmu_init(SysBusDevice *dev)
+static void exynos4210_pmu_init(Object *obj)
{
- Exynos4210PmuState *s = EXYNOS4210_PMU(dev);
+ Exynos4210PmuState *s = EXYNOS4210_PMU(obj);
+ SysBusDevice *dev = SYS_BUS_DEVICE(obj);
/* memory mapping */
- memory_region_init_io(&s->iomem, OBJECT(dev), &exynos4210_pmu_ops, s,
+ memory_region_init_io(&s->iomem, obj, &exynos4210_pmu_ops, s,
"exynos4210.pmu", EXYNOS4210_PMU_REGS_MEM_SIZE);
sysbus_init_mmio(dev, &s->iomem);
- return 0;
}
static const VMStateDescription exynos4210_pmu_vmstate = {
@@ -481,9 +481,7 @@ static const VMStateDescription exynos4210_pmu_vmstate = {
static void exynos4210_pmu_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = exynos4210_pmu_init;
dc->reset = exynos4210_pmu_reset;
dc->vmsd = &exynos4210_pmu_vmstate;
}
@@ -492,6 +490,7 @@ static const TypeInfo exynos4210_pmu_info = {
.name = TYPE_EXYNOS4210_PMU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(Exynos4210PmuState),
+ .instance_init = exynos4210_pmu_init,
.class_init = exynos4210_pmu_class_init,
};
diff --git a/hw/misc/mst_fpga.c b/hw/misc/mst_fpga.c
index 48d7dfb..a10f049 100644
--- a/hw/misc/mst_fpga.c
+++ b/hw/misc/mst_fpga.c
@@ -200,10 +200,11 @@ static int mst_fpga_post_load(void *opaque, int version_id)
return 0;
}
-static int mst_fpga_init(SysBusDevice *sbd)
+static void mst_fpga_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- mst_irq_state *s = MAINSTONE_FPGA(dev);
+ DeviceState *dev = DEVICE(obj);
+ mst_irq_state *s = MAINSTONE_FPGA(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
s->pcmcia0 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD;
s->pcmcia1 = MST_PCMCIAx_READY | MST_PCMCIAx_nCD;
@@ -213,10 +214,9 @@ static int mst_fpga_init(SysBusDevice *sbd)
/* alloc the external 16 irqs */
qdev_init_gpio_in(dev, mst_fpga_set_irq, MST_NUM_IRQS);
- memory_region_init_io(&s->iomem, OBJECT(s), &mst_fpga_ops, s,
+ memory_region_init_io(&s->iomem, obj, &mst_fpga_ops, s,
"fpga", 0x00100000);
sysbus_init_mmio(sbd, &s->iomem);
- return 0;
}
static VMStateDescription vmstate_mst_fpga_regs = {
@@ -245,9 +245,7 @@ static VMStateDescription vmstate_mst_fpga_regs = {
static void mst_fpga_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
- SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
- k->init = mst_fpga_init;
dc->desc = "Mainstone II FPGA";
dc->vmsd = &vmstate_mst_fpga_regs;
}
@@ -256,6 +254,7 @@ static const TypeInfo mst_fpga_info = {
.name = TYPE_MAINSTONE_FPGA,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(mst_irq_state),
+ .instance_init = mst_fpga_init,
.class_init = mst_fpga_class_init,
};
diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c
index eb783c6..82c63a4 100644
--- a/hw/sd/pl181.c
+++ b/hw/sd/pl181.c
@@ -13,6 +13,7 @@
#include "hw/sysbus.h"
#include "hw/sd/sd.h"
#include "qemu/log.h"
+#include "qapi/error.h"
//#define DEBUG_PL181 1
@@ -481,43 +482,48 @@ static void pl181_reset(DeviceState *d)
sd_set_cb(s->card, s->cardstatus[0], s->cardstatus[1]);
}
-static int pl181_init(SysBusDevice *sbd)
+static void pl181_init(Object *obj)
{
- DeviceState *dev = DEVICE(sbd);
- PL181State *s = PL181(dev);
- DriveInfo *dinfo;
+ DeviceState *dev = DEVICE(obj);
+ PL181State *s = PL181(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- memory_region_init_io(&s->iomem, OBJECT(s), &pl181_ops, s, "pl181", 0x1000);
+ memory_region_init_io(&s->iomem, obj, &pl181_ops, s, "pl181", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq[0]);
sysbus_init_irq(sbd, &s->irq[1]);
qdev_init_gpio_out(dev, s->cardstatus, 2);
+}
+
+static void pl181_realize(DeviceState *dev, Error **errp)
+{
+ PL181State *s = PL181(dev);
+ DriveInfo *dinfo;
+
/* FIXME use a qdev drive property instead of drive_get_next() */
dinfo = drive_get_next(IF_SD);
s->card = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, false);
if (s->card == NULL) {
- return -1;
+ error_setg(errp, "sd_init failed");
}
-
- return 0;
}
static void pl181_class_init(ObjectClass *klass, void *data)
{
- SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
DeviceClass *k = DEVICE_CLASS(klass);
- sdc->init = pl181_init;
k->vmsd = &vmstate_pl181;
k->reset = pl181_reset;
/* Reason: init() method uses drive_get_next() */
k->cannot_instantiate_with_device_add_yet = true;
+ k->realize = pl181_realize;
}
static const TypeInfo pl181_info = {
.name = TYPE_PL181,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(PL181State),
+ .instance_init = pl181_init,
.class_init = pl181_class_init,
};
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 82703d2..9650193 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -41,6 +41,10 @@
#define ARCH_TIMER_NS_EL1_IRQ 14
#define ARCH_TIMER_NS_EL2_IRQ 10
+#define VIRTUAL_PMU_IRQ 7
+
+#define PPI(irq) ((irq) + 16)
+
enum {
VIRT_FLASH,
VIRT_MEM,
diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
index 68f6eb0..c2931bf 100644
--- a/include/hw/arm/xlnx-zynqmp.h
+++ b/include/hw/arm/xlnx-zynqmp.h
@@ -26,6 +26,8 @@
#include "hw/ide/ahci.h"
#include "hw/sd/sdhci.h"
#include "hw/ssi/xilinx_spips.h"
+#include "hw/dma/xlnx_dpdma.h"
+#include "hw/display/xlnx_dp.h"
#define TYPE_XLNX_ZYNQMP "xlnx,zynqmp"
#define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \
@@ -81,6 +83,8 @@ typedef struct XlnxZynqMPState {
SysbusAHCIState sata;
SDHCIState sdhci[XLNX_ZYNQMP_NUM_SDHCI];
XilinxSPIPS spi[XLNX_ZYNQMP_NUM_SPIS];
+ XlnxDPState dp;
+ XlnxDPDMAState dpdma;
char *boot_cpu;
ARMCPU *boot_cpu_ptr;
diff --git a/include/hw/display/dpcd.h b/include/hw/display/dpcd.h
new file mode 100644
index 0000000..274dc2e
--- /dev/null
+++ b/include/hw/display/dpcd.h
@@ -0,0 +1,105 @@
+/*
+ * dpcd.h
+ *
+ * Copyright (C)2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DPCD_H
+#define DPCD_H
+
+typedef struct DPCDState DPCDState;
+
+#define TYPE_DPCD "dpcd"
+#define DPCD(obj) OBJECT_CHECK(DPCDState, (obj), TYPE_DPCD)
+
+/* DCPD Revision. */
+#define DPCD_REVISION 0x00
+#define DPCD_REV_1_0 0x10
+#define DPCD_REV_1_1 0x11
+
+/* DCPD Max Link Rate. */
+#define DPCD_MAX_LINK_RATE 0x01
+#define DPCD_1_62GBPS 0x06
+#define DPCD_2_7GBPS 0x0A
+#define DPCD_5_4GBPS 0x14
+
+#define DPCD_MAX_LANE_COUNT 0x02
+#define DPCD_ONE_LANE 0x01
+#define DPCD_TWO_LANES 0x02
+#define DPCD_FOUR_LANES 0x04
+
+/* DCPD Max down spread. */
+#define DPCD_UP_TO_0_5 0x01
+#define DPCD_NO_AUX_HANDSHAKE_LINK_TRAINING 0x40
+
+/* DCPD Downstream port type. */
+#define DPCD_DISPLAY_PORT 0x00
+#define DPCD_ANALOG 0x02
+#define DPCD_DVI_HDMI 0x04
+#define DPCD_OTHER 0x06
+
+/* DPCD Format conversion. */
+#define DPCD_FORMAT_CONVERSION 0x08
+
+/* Main link channel coding. */
+#define DPCD_ANSI_8B_10B 0x01
+
+/* Down stream port count. */
+#define DPCD_OUI_SUPPORTED 0x80
+
+/* Receiver port capability. */
+#define DPCD_RECEIVE_PORT0_CAP_0 0x08
+#define DPCD_RECEIVE_PORT0_CAP_1 0x09
+#define DPCD_EDID_PRESENT 0x02
+#define DPCD_ASSOCIATED_TO_PRECEDING_PORT 0x04
+
+/* Down stream port capability. */
+#define DPCD_CAP_DISPLAY_PORT 0x000
+#define DPCD_CAP_ANALOG_VGA 0x001
+#define DPCD_CAP_DVI 0x002
+#define DPCD_CAP_HDMI 0x003
+#define DPCD_CAP_OTHER 0x100
+
+#define DPCD_LANE0_1_STATUS 0x202
+#define DPCD_LANE0_CR_DONE (1 << 0)
+#define DPCD_LANE0_CHANNEL_EQ_DONE (1 << 1)
+#define DPCD_LANE0_SYMBOL_LOCKED (1 << 2)
+#define DPCD_LANE1_CR_DONE (1 << 4)
+#define DPCD_LANE1_CHANNEL_EQ_DONE (1 << 5)
+#define DPCD_LANE1_SYMBOL_LOCKED (1 << 6)
+
+#define DPCD_LANE2_3_STATUS 0x203
+#define DPCD_LANE2_CR_DONE (1 << 0)
+#define DPCD_LANE2_CHANNEL_EQ_DONE (1 << 1)
+#define DPCD_LANE2_SYMBOL_LOCKED (1 << 2)
+#define DPCD_LANE3_CR_DONE (1 << 4)
+#define DPCD_LANE3_CHANNEL_EQ_DONE (1 << 5)
+#define DPCD_LANE3_SYMBOL_LOCKED (1 << 6)
+
+#define DPCD_LANE_ALIGN_STATUS_UPDATED 0x204
+#define DPCD_INTERLANE_ALIGN_DONE 0x01
+#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED 0x40
+#define DPCD_LINK_STATUS_UPDATED 0x80
+
+#define DPCD_SINK_STATUS 0x205
+#define DPCD_RECEIVE_PORT_0_STATUS 0x01
+
+#endif /* !DPCD_H */
diff --git a/include/hw/display/xlnx_dp.h b/include/hw/display/xlnx_dp.h
new file mode 100644
index 0000000..d3a03f1
--- /dev/null
+++ b/include/hw/display/xlnx_dp.h
@@ -0,0 +1,109 @@
+/*
+ * xlnx_dp.h
+ *
+ * Copyright (C) 2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "hw/sysbus.h"
+#include "ui/console.h"
+#include "hw/misc/aux.h"
+#include "hw/i2c/i2c.h"
+#include "hw/display/dpcd.h"
+#include "hw/i2c/i2c-ddc.h"
+#include "qemu/fifo8.h"
+#include "hw/dma/xlnx_dpdma.h"
+#include "audio/audio.h"
+
+#ifndef XLNX_DP_H
+#define XLNX_DP_H
+
+#define AUD_CHBUF_MAX_DEPTH 32768
+#define MAX_QEMU_BUFFER_SIZE 4096
+
+#define DP_CORE_REG_ARRAY_SIZE (0x3AF >> 2)
+#define DP_AVBUF_REG_ARRAY_SIZE (0x238 >> 2)
+#define DP_VBLEND_REG_ARRAY_SIZE (0x1DF >> 2)
+#define DP_AUDIO_REG_ARRAY_SIZE (0x50 >> 2)
+
+struct PixmanPlane {
+ pixman_format_code_t format;
+ DisplaySurface *surface;
+};
+
+typedef struct XlnxDPState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /* < public >*/
+ MemoryRegion container;
+
+ uint32_t core_registers[DP_CORE_REG_ARRAY_SIZE];
+ MemoryRegion core_iomem;
+
+ uint32_t avbufm_registers[DP_AVBUF_REG_ARRAY_SIZE];
+ MemoryRegion avbufm_iomem;
+
+ uint32_t vblend_registers[DP_VBLEND_REG_ARRAY_SIZE];
+ MemoryRegion vblend_iomem;
+
+ uint32_t audio_registers[DP_AUDIO_REG_ARRAY_SIZE];
+ MemoryRegion audio_iomem;
+
+ QemuConsole *console;
+
+ /*
+ * This is the planes used to display in console. When the blending is
+ * enabled bout_plane is displayed in console else it's g_plane.
+ */
+ struct PixmanPlane g_plane;
+ struct PixmanPlane v_plane;
+ struct PixmanPlane bout_plane;
+
+ QEMUSoundCard aud_card;
+ SWVoiceOut *amixer_output_stream;
+ int16_t audio_buffer_0[AUD_CHBUF_MAX_DEPTH];
+ int16_t audio_buffer_1[AUD_CHBUF_MAX_DEPTH];
+ size_t audio_data_available[2];
+ int64_t temp_buffer[AUD_CHBUF_MAX_DEPTH];
+ int16_t out_buffer[AUD_CHBUF_MAX_DEPTH];
+ size_t byte_left; /* byte available in out_buffer. */
+ size_t data_ptr; /* next byte to be sent to QEMU. */
+
+ /* Associated DPDMA controller. */
+ XlnxDPDMAState *dpdma;
+
+ qemu_irq irq;
+
+ AUXBus *aux_bus;
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+
+ /*
+ * XXX: This should be in an other module.
+ */
+ DPCDState *dpcd;
+ I2CDDCState *edid;
+} XlnxDPState;
+
+#define TYPE_XLNX_DP "xlnx.v-dp"
+#define XLNX_DP(obj) OBJECT_CHECK(XlnxDPState, (obj), TYPE_XLNX_DP)
+
+#endif /* !XLNX_DP_H */
diff --git a/include/hw/dma/xlnx_dpdma.h b/include/hw/dma/xlnx_dpdma.h
new file mode 100644
index 0000000..ae571a0
--- /dev/null
+++ b/include/hw/dma/xlnx_dpdma.h
@@ -0,0 +1,85 @@
+/*
+ * xlnx_dpdma.h
+ *
+ * Copyright (C) 2015 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef XLNX_DPDMA_H
+#define XLNX_DPDMA_H
+
+#include "hw/sysbus.h"
+#include "ui/console.h"
+#include "sysemu/dma.h"
+
+#define XLNX_DPDMA_REG_ARRAY_SIZE (0x1000 >> 2)
+
+struct XlnxDPDMAState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+ MemoryRegion iomem;
+ uint32_t registers[XLNX_DPDMA_REG_ARRAY_SIZE];
+ uint8_t *data[6];
+ bool operation_finished[6];
+ qemu_irq irq;
+};
+
+typedef struct XlnxDPDMAState XlnxDPDMAState;
+
+#define TYPE_XLNX_DPDMA "xlnx.dpdma"
+#define XLNX_DPDMA(obj) OBJECT_CHECK(XlnxDPDMAState, (obj), TYPE_XLNX_DPDMA)
+
+/*
+ * xlnx_dpdma_start_operation: Start the operation on the specified channel. The
+ * DPDMA gets the current descriptor and retrieves
+ * data to the buffer specified by
+ * dpdma_set_host_data_location().
+ *
+ * Returns The number of bytes transfered by the DPDMA or 0 if an error occured.
+ *
+ * @s The DPDMA state.
+ * @channel The channel to start.
+ */
+size_t xlnx_dpdma_start_operation(XlnxDPDMAState *s, uint8_t channel,
+ bool one_desc);
+
+/*
+ * xlnx_dpdma_set_host_data_location: Set the location in the host memory where
+ * to store the data out from the dma
+ * channel.
+ *
+ * @s The DPDMA state.
+ * @channel The channel associated to the pointer.
+ * @p The buffer where to store the data.
+ */
+/* XXX: add a maximum size arg and send an interrupt in case of overflow. */
+void xlnx_dpdma_set_host_data_location(XlnxDPDMAState *s, uint8_t channel,
+ void *p);
+
+/*
+ * xlnx_dpdma_trigger_vsync_irq: Trigger a VSYNC IRQ when the display is
+ * updated.
+ *
+ * @s The DPDMA state.
+ */
+void xlnx_dpdma_trigger_vsync_irq(XlnxDPDMAState *s);
+
+#endif /* !XLNX_DPDMA_H */
diff --git a/include/hw/i2c/i2c-ddc.h b/include/hw/i2c/i2c-ddc.h
new file mode 100644
index 0000000..cb8e62d
--- /dev/null
+++ b/include/hw/i2c/i2c-ddc.h
@@ -0,0 +1,38 @@
+/* A simple I2C slave for returning monitor EDID data via DDC.
+ *
+ * Copyright (c) 2011 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef I2C_DDC
+#define I2C_DDC
+
+/* A simple I2C slave which just returns the contents of its EDID blob. */
+
+struct I2CDDCState {
+ /*< private >*/
+ I2CSlave i2c;
+ /*< public >*/
+ bool firstbyte;
+ uint8_t reg;
+ uint8_t edid_blob[128];
+};
+
+typedef struct I2CDDCState I2CDDCState;
+
+#define TYPE_I2CDDC "i2c-ddc"
+#define I2CDDC(obj) OBJECT_CHECK(I2CDDCState, (obj), TYPE_I2CDDC)
+
+#endif /* !I2C_DDC */
diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h
index 4986ebc..c4085aa 100644
--- a/include/hw/i2c/i2c.h
+++ b/include/hw/i2c/i2c.h
@@ -56,6 +56,7 @@ int i2c_bus_busy(I2CBus *bus);
int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv);
void i2c_end_transfer(I2CBus *bus);
void i2c_nack(I2CBus *bus);
+int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send);
int i2c_send(I2CBus *bus, uint8_t data);
int i2c_recv(I2CBus *bus);
diff --git a/include/hw/misc/aux.h b/include/hw/misc/aux.h
new file mode 100644
index 0000000..759c3bf
--- /dev/null
+++ b/include/hw/misc/aux.h
@@ -0,0 +1,128 @@
+/*
+ * aux.h
+ *
+ * Copyright (C)2014 : GreenSocs Ltd
+ * http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ * Developed by :
+ * Frederic Konrad <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QEMU_AUX_H
+#define QEMU_AUX_H
+
+#include "hw/qdev.h"
+
+typedef struct AUXBus AUXBus;
+typedef struct AUXSlave AUXSlave;
+typedef enum AUXCommand AUXCommand;
+typedef enum AUXReply AUXReply;
+typedef struct AUXTOI2CState AUXTOI2CState;
+
+enum AUXCommand {
+ WRITE_I2C = 0,
+ READ_I2C = 1,
+ WRITE_I2C_STATUS = 2,
+ WRITE_I2C_MOT = 4,
+ READ_I2C_MOT = 5,
+ WRITE_AUX = 8,
+ READ_AUX = 9
+};
+
+enum AUXReply {
+ AUX_I2C_ACK = 0,
+ AUX_NACK = 1,
+ AUX_DEFER = 2,
+ AUX_I2C_NACK = 4,
+ AUX_I2C_DEFER = 8
+};
+
+#define TYPE_AUX_BUS "aux-bus"
+#define AUX_BUS(obj) OBJECT_CHECK(AUXBus, (obj), TYPE_AUX_BUS)
+
+struct AUXBus {
+ /* < private > */
+ BusState qbus;
+
+ /* < public > */
+ AUXSlave *current_dev;
+ AUXSlave *dev;
+ uint32_t last_i2c_address;
+ AUXCommand last_transaction;
+
+ AUXTOI2CState *bridge;
+
+ MemoryRegion *aux_io;
+ AddressSpace aux_addr_space;
+};
+
+#define TYPE_AUX_SLAVE "aux-slave"
+#define AUX_SLAVE(obj) \
+ OBJECT_CHECK(AUXSlave, (obj), TYPE_AUX_SLAVE)
+
+struct AUXSlave {
+ /* < private > */
+ DeviceState parent_obj;
+
+ /* < public > */
+ MemoryRegion *mmio;
+};
+
+/**
+ * aux_init_bus: Initialize an AUX bus.
+ *
+ * Returns the new AUX bus created.
+ *
+ * @parent The device where this bus is located.
+ * @name The name of the bus.
+ */
+AUXBus *aux_init_bus(DeviceState *parent, const char *name);
+
+/*
+ * aux_request: Make a request on the bus.
+ *
+ * Returns the reply of the request.
+ *
+ * @bus Ths bus where the request happen.
+ * @cmd The command requested.
+ * @address The 20bits address of the slave.
+ * @len The length of the read or write.
+ * @data The data array which will be filled or read during transfer.
+ */
+AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
+ uint8_t len, uint8_t *data);
+
+/*
+ * aux_get_i2c_bus: Get the i2c bus for I2C over AUX command.
+ *
+ * Returns the i2c bus associated to this AUX bus.
+ *
+ * @bus The AUX bus.
+ */
+I2CBus *aux_get_i2c_bus(AUXBus *bus);
+
+/*
+ * aux_init_mmio: Init an mmio for an AUX slave.
+ *
+ * @aux_slave The AUX slave.
+ * @mmio The mmio to be registered.
+ */
+void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio);
+
+DeviceState *aux_create_slave(AUXBus *bus, const char *name, uint32_t addr);
+
+#endif /* !QEMU_AUX_H */
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 17d8051..942aa36 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -572,6 +572,8 @@ struct ARMCPU {
bool powered_off;
/* CPU has security extension */
bool has_el3;
+ /* CPU has PMU (Performance Monitor Unit) */
+ bool has_pmu;
/* CPU has memory protection unit */
bool has_mpu;
diff --git a/target-arm/helper.c b/target-arm/helper.c
index 862e780..c9730d6 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -3765,8 +3765,11 @@ static const ARMCPRegInfo el3_cp_reginfo[] = {
.opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 2,
.access = PL3_RW,
/* no .writefn needed as this can't cause an ASID change;
- * no .raw_writefn or .resetfn needed as we never use mask/base_mask
+ * we must provide a .raw_writefn and .resetfn because we handle
+ * reset and migration for the AArch32 TTBCR(S), which might be
+ * using mask and base_mask.
*/
+ .resetfn = vmsa_ttbcr_reset, .raw_writefn = vmsa_ttbcr_raw_write,
.fieldoffset = offsetof(CPUARMState, cp15.tcr_el[3]) },
{ .name = "ELR_EL3", .state = ARM_CP_STATE_AA64,
.type = ARM_CP_ALIAS,
diff --git a/target-arm/kvm32.c b/target-arm/kvm32.c
index c03e3e5..c35c676 100644
--- a/target-arm/kvm32.c
+++ b/target-arm/kvm32.c
@@ -522,3 +522,9 @@ bool kvm_arm_hw_debug_active(CPUState *cs)
{
return false;
}
+
+int kvm_arm_pmu_create(CPUState *cs, int irq)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+ return 0;
+}
diff --git a/target-arm/kvm64.c b/target-arm/kvm64.c
index e2a34f6..2d6a310 100644
--- a/target-arm/kvm64.c
+++ b/target-arm/kvm64.c
@@ -382,6 +382,47 @@ static CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr)
return NULL;
}
+static bool kvm_arm_pmu_support_ctrl(CPUState *cs, struct kvm_device_attr *attr)
+{
+ return kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr) == 0;
+}
+
+int kvm_arm_pmu_create(CPUState *cs, int irq)
+{
+ int err;
+
+ struct kvm_device_attr attr = {
+ .group = KVM_ARM_VCPU_PMU_V3_CTRL,
+ .addr = (intptr_t)&irq,
+ .attr = KVM_ARM_VCPU_PMU_V3_IRQ,
+ .flags = 0,
+ };
+
+ if (!kvm_arm_pmu_support_ctrl(cs, &attr)) {
+ return 0;
+ }
+
+ err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, &attr);
+ if (err < 0) {
+ fprintf(stderr, "KVM_SET_DEVICE_ATTR failed: %s\n",
+ strerror(-err));
+ abort();
+ }
+
+ attr.group = KVM_ARM_VCPU_PMU_V3_CTRL;
+ attr.attr = KVM_ARM_VCPU_PMU_V3_INIT;
+ attr.addr = 0;
+ attr.flags = 0;
+
+ err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, &attr);
+ if (err < 0) {
+ fprintf(stderr, "KVM_SET_DEVICE_ATTR failed: %s\n",
+ strerror(-err));
+ abort();
+ }
+
+ return 1;
+}
static inline void set_feature(uint64_t *features, int feature)
{
@@ -461,6 +502,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
if (!arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_EL1_32BIT;
}
+ if (kvm_irqchip_in_kernel() &&
+ kvm_check_extension(cs->kvm_state, KVM_CAP_ARM_PMU_V3)) {
+ cpu->has_pmu = true;
+ cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_PMU_V3;
+ }
/* Do KVM_ARM_VCPU_INIT ioctl */
ret = kvm_arm_vcpu_init(cs);
diff --git a/target-arm/kvm_arm.h b/target-arm/kvm_arm.h
index 345233c..a419368 100644
--- a/target-arm/kvm_arm.h
+++ b/target-arm/kvm_arm.h
@@ -194,6 +194,8 @@ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu);
int kvm_arm_vgic_probe(void);
+int kvm_arm_pmu_create(CPUState *cs, int irq);
+
#else
static inline int kvm_arm_vgic_probe(void)
@@ -201,6 +203,11 @@ static inline int kvm_arm_vgic_probe(void)
return 0;
}
+static inline int kvm_arm_pmu_create(CPUState *cs, int irq)
+{
+ return 0;
+}
+
#endif
static inline const char *gic_class_name(void)
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 6815bc1a..3e71467 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -5311,6 +5311,30 @@ static int neon_2rm_is_float_op(int op)
op >= NEON_2RM_VRECPE_F);
}
+static bool neon_2rm_is_v8_op(int op)
+{
+ /* Return true if this neon 2reg-misc op is ARMv8 and up */
+ switch (op) {
+ case NEON_2RM_VRINTN:
+ case NEON_2RM_VRINTA:
+ case NEON_2RM_VRINTM:
+ case NEON_2RM_VRINTP:
+ case NEON_2RM_VRINTZ:
+ case NEON_2RM_VRINTX:
+ case NEON_2RM_VCVTAU:
+ case NEON_2RM_VCVTAS:
+ case NEON_2RM_VCVTNU:
+ case NEON_2RM_VCVTNS:
+ case NEON_2RM_VCVTPU:
+ case NEON_2RM_VCVTPS:
+ case NEON_2RM_VCVTMU:
+ case NEON_2RM_VCVTMS:
+ return true;
+ default:
+ return false;
+ }
+}
+
/* Each entry in this array has bit n set if the insn allows
* size value n (otherwise it will UNDEF). Since unallocated
* op values will have no bits set they always UNDEF.
@@ -6798,6 +6822,10 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
if ((neon_2rm_sizes[op] & (1 << size)) == 0) {
return 1;
}
+ if (neon_2rm_is_v8_op(op) &&
+ !arm_dc_feature(s, ARM_FEATURE_V8)) {
+ return 1;
+ }
if ((op != NEON_2RM_VMOVN && op != NEON_2RM_VQMOVN) &&
q && ((rm | rd) & 1)) {
return 1;