From a2a8dfa8d830bf8482fdabcefdc9005189c9ecdb Mon Sep 17 00:00:00 2001 From: Andrew Baumann Date: Wed, 16 Mar 2016 17:06:01 +0000 Subject: bcm2835_peripherals: enable sdhci pending-insert quirk for raspberry pi Signed-off-by: Andrew Baumann Reviewed-by: Peter Maydell Message-id: 1457467526-8840-2-git-send-email-Andrew.Baumann@microsoft.com Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'hw/arm/bcm2835_peripherals.c') diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 6d66fa0280..6ce9cd1bd6 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -171,6 +171,13 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } + object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk", + &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err); if (err) { error_propagate(errp, err); -- cgit 1.4.1 From 97398d900caaccbf1b6cc53a80581b69687757e7 Mon Sep 17 00:00:00 2001 From: Andrew Baumann Date: Wed, 16 Mar 2016 17:06:01 +0000 Subject: bcm2835_aux: add emulation of BCM2835 AUX (aka UART1) block At present only the core UART functions (data path for tx/rx) are implemented, which is enough for UEFI to boot. The following features/registers are unimplemented: * Line/modem control * Scratch register * Extra control * Baudrate * SPI interfaces Signed-off-by: Andrew Baumann Reviewed-by: Peter Maydell Message-id: 1457467526-8840-3-git-send-email-Andrew.Baumann@microsoft.com Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 32 ++++ hw/char/Makefile.objs | 1 + hw/char/bcm2835_aux.c | 316 +++++++++++++++++++++++++++++++++++ include/hw/arm/bcm2835_peripherals.h | 2 + include/hw/char/bcm2835_aux.h | 33 ++++ 5 files changed, 384 insertions(+) create mode 100644 hw/char/bcm2835_aux.c create mode 100644 include/hw/char/bcm2835_aux.h (limited to 'hw/arm/bcm2835_peripherals.c') diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 6ce9cd1bd6..d6453cc5ab 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -12,6 +12,7 @@ #include "hw/arm/bcm2835_peripherals.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" +#include "sysemu/char.h" /* Peripheral base address on the VC (GPU) system bus */ #define BCM2835_VC_PERI_BASE 0x7e000000 @@ -48,6 +49,11 @@ static void bcm2835_peripherals_init(Object *obj) object_property_add_child(obj, "uart0", OBJECT(s->uart0), NULL); qdev_set_parent_bus(DEVICE(s->uart0), sysbus_get_default()); + /* AUX / UART1 */ + object_initialize(&s->aux, sizeof(s->aux), TYPE_BCM2835_AUX); + object_property_add_child(obj, "aux", OBJECT(&s->aux), NULL); + qdev_set_parent_bus(DEVICE(&s->aux), sysbus_get_default()); + /* Mailboxes */ object_initialize(&s->mboxes, sizeof(s->mboxes), TYPE_BCM2835_MBOX); object_property_add_child(obj, "mbox", OBJECT(&s->mboxes), NULL); @@ -79,6 +85,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) MemoryRegion *ram; Error *err = NULL; uint32_t ram_size; + CharDriverState *chr; int n; obj = object_property_get_link(OBJECT(dev), "ram", &err); @@ -131,6 +138,29 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_UART)); + /* AUX / UART1 */ + /* TODO: don't call qemu_char_get_next_serial() here, instead set + * chardev properties for each uart at the board level, once pl011 + * (uart0) has been updated to avoid qemu_char_get_next_serial() + */ + chr = qemu_char_get_next_serial(); + if (chr == NULL) { + chr = qemu_chr_new("bcm2835.uart1", "null", NULL); + } + qdev_prop_set_chr(DEVICE(&s->aux), "chardev", chr); + + object_property_set_bool(OBJECT(&s->aux), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, UART1_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0, + qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, + INTERRUPT_AUX)); + /* Mailboxes */ object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err); if (err) { @@ -203,6 +233,8 @@ static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = bcm2835_peripherals_realize; + /* Reason: realize() method uses qemu_char_get_next_serial() */ + dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo bcm2835_peripherals_type_info = { diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 5931cc8400..69a553cd8d 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -16,6 +16,7 @@ obj-$(CONFIG_SH4) += sh_serial.o obj-$(CONFIG_PSERIES) += spapr_vty.o obj-$(CONFIG_DIGIC) += digic-uart.o obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o +obj-$(CONFIG_RASPI) += bcm2835_aux.o common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c new file mode 100644 index 0000000000..0394d11a88 --- /dev/null +++ b/hw/char/bcm2835_aux.c @@ -0,0 +1,316 @@ +/* + * BCM2835 (Raspberry Pi / Pi 2) Aux block (mini UART and SPI). + * Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * Based on pl011.c, copyright terms below: + * + * Arm PrimeCell PL011 UART + * + * Copyright (c) 2006 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + * + * At present only the core UART functions (data path for tx/rx) are + * implemented. The following features/registers are unimplemented: + * - Line/modem control + * - Scratch register + * - Extra control + * - Baudrate + * - SPI interfaces + */ + +#include "qemu/osdep.h" +#include "hw/char/bcm2835_aux.h" + +#define AUX_IRQ 0x0 +#define AUX_ENABLES 0x4 +#define AUX_MU_IO_REG 0x40 +#define AUX_MU_IER_REG 0x44 +#define AUX_MU_IIR_REG 0x48 +#define AUX_MU_LCR_REG 0x4c +#define AUX_MU_MCR_REG 0x50 +#define AUX_MU_LSR_REG 0x54 +#define AUX_MU_MSR_REG 0x58 +#define AUX_MU_SCRATCH 0x5c +#define AUX_MU_CNTL_REG 0x60 +#define AUX_MU_STAT_REG 0x64 +#define AUX_MU_BAUD_REG 0x68 + +/* bits in IER/IIR registers */ +#define TX_INT 0x1 +#define RX_INT 0x2 + +static void bcm2835_aux_update(BCM2835AuxState *s) +{ + /* signal an interrupt if either: + * 1. rx interrupt is enabled and we have a non-empty rx fifo, or + * 2. the tx interrupt is enabled (since we instantly drain the tx fifo) + */ + s->iir = 0; + if ((s->ier & RX_INT) && s->read_count != 0) { + s->iir |= RX_INT; + } + if (s->ier & TX_INT) { + s->iir |= TX_INT; + } + qemu_set_irq(s->irq, s->iir != 0); +} + +static uint64_t bcm2835_aux_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835AuxState *s = opaque; + uint32_t c, res; + + switch (offset) { + case AUX_IRQ: + return s->iir != 0; + + case AUX_ENABLES: + return 1; /* mini UART permanently enabled */ + + case AUX_MU_IO_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + c = s->read_fifo[s->read_pos]; + if (s->read_count > 0) { + s->read_count--; + if (++s->read_pos == BCM2835_AUX_RX_FIFO_LEN) { + s->read_pos = 0; + } + } + if (s->chr) { + qemu_chr_accept_input(s->chr); + } + bcm2835_aux_update(s); + return c; + + case AUX_MU_IER_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + return 0xc0 | s->ier; /* FIFO enables always read 1 */ + + case AUX_MU_IIR_REG: + res = 0xc0; /* FIFO enables */ + /* The spec is unclear on what happens when both tx and rx + * interrupts are active, besides that this cannot occur. At + * present, we choose to prioritise the rx interrupt, since + * the tx fifo is always empty. */ + if (s->read_count != 0) { + res |= 0x4; + } else { + res |= 0x2; + } + if (s->iir == 0) { + res |= 0x1; + } + return res; + + case AUX_MU_LCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_MCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_LSR_REG: + res = 0x60; /* tx idle, empty */ + if (s->read_count != 0) { + res |= 0x1; + } + return res; + + case AUX_MU_MSR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MSR_REG unsupported\n", __func__); + return 0; + + case AUX_MU_SCRATCH: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__); + return 0; + + case AUX_MU_CNTL_REG: + return 0x3; /* tx, rx enabled */ + + case AUX_MU_STAT_REG: + res = 0x30e; /* space in the output buffer, empty tx fifo, idle tx/rx */ + if (s->read_count > 0) { + res |= 0x1; /* data in input buffer */ + assert(s->read_count < BCM2835_AUX_RX_FIFO_LEN); + res |= ((uint32_t)s->read_count) << 16; /* rx fifo fill level */ + } + return res; + + case AUX_MU_BAUD_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__); + return 0; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } +} + +static void bcm2835_aux_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835AuxState *s = opaque; + unsigned char ch; + + switch (offset) { + case AUX_ENABLES: + if (value != 1) { + qemu_log_mask(LOG_UNIMP, "%s: unsupported attempt to enable SPI " + "or disable UART\n", __func__); + } + break; + + case AUX_MU_IO_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + ch = value; + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + break; + + case AUX_MU_IER_REG: + /* "DLAB bit set means access baudrate register" is NYI */ + s->ier = value & (TX_INT | RX_INT); + bcm2835_aux_update(s); + break; + + case AUX_MU_IIR_REG: + if (value & 0x2) { + s->read_count = 0; + } + break; + + case AUX_MU_LCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_LCR_REG unsupported\n", __func__); + break; + + case AUX_MU_MCR_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_MCR_REG unsupported\n", __func__); + break; + + case AUX_MU_SCRATCH: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_SCRATCH unsupported\n", __func__); + break; + + case AUX_MU_CNTL_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_CNTL_REG unsupported\n", __func__); + break; + + case AUX_MU_BAUD_REG: + qemu_log_mask(LOG_UNIMP, "%s: AUX_MU_BAUD_REG unsupported\n", __func__); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + } + + bcm2835_aux_update(s); +} + +static int bcm2835_aux_can_receive(void *opaque) +{ + BCM2835AuxState *s = opaque; + + return s->read_count < BCM2835_AUX_RX_FIFO_LEN; +} + +static void bcm2835_aux_put_fifo(void *opaque, uint8_t value) +{ + BCM2835AuxState *s = opaque; + int slot; + + slot = s->read_pos + s->read_count; + if (slot >= BCM2835_AUX_RX_FIFO_LEN) { + slot -= BCM2835_AUX_RX_FIFO_LEN; + } + s->read_fifo[slot] = value; + s->read_count++; + if (s->read_count == BCM2835_AUX_RX_FIFO_LEN) { + /* buffer full */ + } + bcm2835_aux_update(s); +} + +static void bcm2835_aux_receive(void *opaque, const uint8_t *buf, int size) +{ + bcm2835_aux_put_fifo(opaque, *buf); +} + +static const MemoryRegionOps bcm2835_aux_ops = { + .read = bcm2835_aux_read, + .write = bcm2835_aux_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_aux = { + .name = TYPE_BCM2835_AUX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(read_fifo, BCM2835AuxState, + BCM2835_AUX_RX_FIFO_LEN), + VMSTATE_UINT8(read_pos, BCM2835AuxState), + VMSTATE_UINT8(read_count, BCM2835AuxState), + VMSTATE_UINT8(ier, BCM2835AuxState), + VMSTATE_UINT8(iir, BCM2835AuxState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_aux_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + BCM2835AuxState *s = BCM2835_AUX(obj); + + memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_aux_ops, s, + TYPE_BCM2835_AUX, 0x100); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static void bcm2835_aux_realize(DeviceState *dev, Error **errp) +{ + BCM2835AuxState *s = BCM2835_AUX(dev); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, bcm2835_aux_can_receive, + bcm2835_aux_receive, NULL, s); + } +} + +static Property bcm2835_aux_props[] = { + DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bcm2835_aux_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = bcm2835_aux_realize; + dc->vmsd = &vmstate_bcm2835_aux; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + dc->props = bcm2835_aux_props; +} + +static const TypeInfo bcm2835_aux_info = { + .name = TYPE_BCM2835_AUX, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835AuxState), + .instance_init = bcm2835_aux_init, + .class_init = bcm2835_aux_class_init, +}; + +static void bcm2835_aux_register_types(void) +{ + type_register_static(&bcm2835_aux_info); +} + +type_init(bcm2835_aux_register_types) diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 5d888dca53..889adf56b3 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -14,6 +14,7 @@ #include "qemu-common.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" +#include "hw/char/bcm2835_aux.h" #include "hw/intc/bcm2835_ic.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_mbox.h" @@ -33,6 +34,7 @@ typedef struct BCM2835PeripheralState { qemu_irq irq, fiq; SysBusDevice *uart0; + BCM2835AuxState aux; BCM2835ICState ic; BCM2835PropertyState property; BCM2835MboxState mboxes; diff --git a/include/hw/char/bcm2835_aux.h b/include/hw/char/bcm2835_aux.h new file mode 100644 index 0000000000..42f0ee7a92 --- /dev/null +++ b/include/hw/char/bcm2835_aux.h @@ -0,0 +1,33 @@ +/* + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_AUX_H +#define BCM2835_AUX_H + +#include "hw/sysbus.h" +#include "sysemu/char.h" + +#define TYPE_BCM2835_AUX "bcm2835-aux" +#define BCM2835_AUX(obj) OBJECT_CHECK(BCM2835AuxState, (obj), TYPE_BCM2835_AUX) + +#define BCM2835_AUX_RX_FIFO_LEN 8 + +typedef struct { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion iomem; + CharDriverState *chr; + qemu_irq irq; + + uint8_t read_fifo[BCM2835_AUX_RX_FIFO_LEN]; + uint8_t read_pos, read_count; + uint8_t ier, iir; +} BCM2835AuxState; + +#endif -- cgit 1.4.1 From 5e9c2a8dac19d5df0c541ea23df654062de917c0 Mon Sep 17 00:00:00 2001 From: Grégory ESTRADE Date: Wed, 16 Mar 2016 17:06:01 +0000 Subject: bcm2835_fb: add framebuffer device for Raspberry Pi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The framebuffer occupies the upper portion of memory (64MiB by default), but it can only be controlled/configured via a system mailbox or property channel (to be added by a subsequent patch). Signed-off-by: Grégory ESTRADE Reviewed-by: Peter Maydell Signed-off-by: Andrew Baumann Message-id: 1457467526-8840-4-git-send-email-Andrew.Baumann@microsoft.com [AB: added Windows (BGR) support and cleanup/refactoring for upstream submission] Signed-off-by: Andrew Baumann Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 38 +++- hw/arm/bcm2836.c | 2 + hw/arm/raspi.c | 5 +- hw/display/Makefile.objs | 1 + hw/display/bcm2835_fb.c | 424 +++++++++++++++++++++++++++++++++++ include/hw/arm/bcm2835_peripherals.h | 2 + include/hw/display/bcm2835_fb.h | 47 ++++ 7 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 hw/display/bcm2835_fb.c create mode 100644 include/hw/display/bcm2835_fb.h (limited to 'hw/arm/bcm2835_peripherals.c') diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index d6453cc5ab..c2fe6b7625 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -62,6 +62,16 @@ static void bcm2835_peripherals_init(Object *obj) object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr", OBJECT(&s->mbox_mr), &error_abort); + /* Framebuffer */ + object_initialize(&s->fb, sizeof(s->fb), TYPE_BCM2835_FB); + object_property_add_child(obj, "fb", OBJECT(&s->fb), NULL); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size", + &error_abort); + qdev_set_parent_bus(DEVICE(&s->fb), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->fb), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); + /* Property channel */ object_initialize(&s->property, sizeof(s->property), TYPE_BCM2835_PROPERTY); object_property_add_child(obj, "property", OBJECT(&s->property), NULL); @@ -84,7 +94,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) Object *obj; MemoryRegion *ram; Error *err = NULL; - uint32_t ram_size; + uint32_t ram_size, vcram_size; CharDriverState *chr; int n; @@ -174,6 +184,32 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ, INTERRUPT_ARM_MAILBOX)); + /* Framebuffer */ + vcram_size = (uint32_t)object_property_get_int(OBJECT(s), "vcram-size", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_int(OBJECT(&s->fb), ram_size - vcram_size, + "vcram-base", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->fb), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0, + qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB)); + /* Property channel */ object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err); if (err) { diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 032143905e..89a6b35b81 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -42,6 +42,8 @@ static void bcm2836_init(Object *obj) &error_abort); object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), "board-rev", &error_abort); + object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), + "vcram-size", &error_abort); qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default()); } diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 65822792fe..54982095b2 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -113,6 +113,7 @@ static void setup_boot(MachineState *machine, int version, size_t ram_size) static void raspi2_init(MachineState *machine) { RasPiState *s = g_new0(RasPiState, 1); + uint32_t vcram_size; DriveInfo *di; BlockBackend *blk; BusState *bus; @@ -149,7 +150,9 @@ static void raspi2_init(MachineState *machine) qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); - setup_boot(machine, 2, machine->ram_size); + vcram_size = object_property_get_int(OBJECT(&s->soc), "vcram-size", + &error_abort); + setup_boot(machine, 2, machine->ram_size - vcram_size); } static void raspi2_machine_init(MachineClass *mc) diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index f0cf431a0f..d99780eeb9 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -27,6 +27,7 @@ endif obj-$(CONFIG_OMAP) += omap_dss.o obj-$(CONFIG_OMAP) += omap_lcdc.o obj-$(CONFIG_PXA2XX) += pxa2xx_lcd.o +obj-$(CONFIG_RASPI) += bcm2835_fb.o obj-$(CONFIG_SM501) += sm501.o obj-$(CONFIG_TCX) += tcx.o obj-$(CONFIG_CG3) += cg3.o diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c new file mode 100644 index 0000000000..779b56f07b --- /dev/null +++ b/hw/display/bcm2835_fb.c @@ -0,0 +1,424 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Refactoring for Pi2 Copyright (c) 2015, Microsoft. Written by Andrew Baumann. + * This code is licensed under the GNU GPLv2 and later. + * + * Heavily based on milkymist-vgafb.c, copyright terms below: + * QEMU model of the Milkymist VGA framebuffer. + * + * Copyright (c) 2010-2012 Michael Walle + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "hw/display/bcm2835_fb.h" +#include "hw/display/framebuffer.h" +#include "ui/pixel_ops.h" +#include "hw/misc/bcm2835_mbox_defs.h" + +#define DEFAULT_VCRAM_SIZE 0x4000000 +#define BCM2835_FB_OFFSET 0x00100000 + +static void fb_invalidate_display(void *opaque) +{ + BCM2835FBState *s = BCM2835_FB(opaque); + + s->invalidate = true; +} + +static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, + int width, int deststep) +{ + BCM2835FBState *s = opaque; + uint16_t rgb565; + uint32_t rgb888; + uint8_t r, g, b; + DisplaySurface *surface = qemu_console_surface(s->con); + int bpp = surface_bits_per_pixel(surface); + + while (width--) { + switch (s->bpp) { + case 8: + /* lookup palette starting at video ram base + * TODO: cache translation, rather than doing this each time! + */ + rgb888 = ldl_le_phys(&s->dma_as, s->vcram_base + (*src << 2)); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src++; + break; + case 16: + rgb565 = lduw_le_p(src); + r = ((rgb565 >> 11) & 0x1f) << 3; + g = ((rgb565 >> 5) & 0x3f) << 2; + b = ((rgb565 >> 0) & 0x1f) << 3; + src += 2; + break; + case 24: + rgb888 = ldl_le_p(src); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src += 3; + break; + case 32: + rgb888 = ldl_le_p(src); + r = (rgb888 >> 0) & 0xff; + g = (rgb888 >> 8) & 0xff; + b = (rgb888 >> 16) & 0xff; + src += 4; + break; + default: + r = 0; + g = 0; + b = 0; + break; + } + + if (s->pixo == 0) { + /* swap to BGR pixel format */ + uint8_t tmp = r; + r = b; + b = tmp; + } + + switch (bpp) { + case 8: + *dst++ = rgb_to_pixel8(r, g, b); + break; + case 15: + *(uint16_t *)dst = rgb_to_pixel15(r, g, b); + dst += 2; + break; + case 16: + *(uint16_t *)dst = rgb_to_pixel16(r, g, b); + dst += 2; + break; + case 24: + rgb888 = rgb_to_pixel24(r, g, b); + *dst++ = rgb888 & 0xff; + *dst++ = (rgb888 >> 8) & 0xff; + *dst++ = (rgb888 >> 16) & 0xff; + break; + case 32: + *(uint32_t *)dst = rgb_to_pixel32(r, g, b); + dst += 4; + break; + default: + return; + } + } +} + +static void fb_update_display(void *opaque) +{ + BCM2835FBState *s = opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int first = 0; + int last = 0; + int src_width = 0; + int dest_width = 0; + + if (s->lock || !s->xres) { + return; + } + + src_width = s->xres * (s->bpp >> 3); + dest_width = s->xres; + + switch (surface_bits_per_pixel(surface)) { + case 0: + return; + case 8: + break; + case 15: + dest_width *= 2; + break; + case 16: + dest_width *= 2; + break; + case 24: + dest_width *= 3; + break; + case 32: + dest_width *= 4; + break; + default: + hw_error("bcm2835_fb: bad color depth\n"); + break; + } + + if (s->invalidate) { + framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base, + s->yres, src_width); + } + + framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, + src_width, dest_width, 0, s->invalidate, + draw_line_src16, s, &first, &last); + + if (first >= 0) { + dpy_gfx_update(s->con, 0, first, s->xres, last - first + 1); + } + + s->invalidate = false; +} + +static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) +{ + value &= ~0xf; + + s->lock = true; + + s->xres = ldl_le_phys(&s->dma_as, value); + s->yres = ldl_le_phys(&s->dma_as, value + 4); + s->xres_virtual = ldl_le_phys(&s->dma_as, value + 8); + s->yres_virtual = ldl_le_phys(&s->dma_as, value + 12); + s->bpp = ldl_le_phys(&s->dma_as, value + 20); + s->xoffset = ldl_le_phys(&s->dma_as, value + 24); + s->yoffset = ldl_le_phys(&s->dma_as, value + 28); + + s->base = s->vcram_base | (value & 0xc0000000); + s->base += BCM2835_FB_OFFSET; + + /* TODO - Manage properly virtual resolution */ + + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + stl_le_phys(&s->dma_as, value + 16, s->pitch); + stl_le_phys(&s->dma_as, value + 32, s->base); + stl_le_phys(&s->dma_as, value + 36, s->size); + + s->invalidate = true; + qemu_console_resize(s->con, s->xres, s->yres); + s->lock = false; +} + +void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, + uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, + uint32_t *pixo, uint32_t *alpha) +{ + s->lock = true; + + /* TODO: input validation! */ + if (xres) { + s->xres = *xres; + } + if (yres) { + s->yres = *yres; + } + if (xoffset) { + s->xoffset = *xoffset; + } + if (yoffset) { + s->yoffset = *yoffset; + } + if (bpp) { + s->bpp = *bpp; + } + if (pixo) { + s->pixo = *pixo; + } + if (alpha) { + s->alpha = *alpha; + } + + /* TODO - Manage properly virtual resolution */ + + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + s->invalidate = true; + qemu_console_resize(s->con, s->xres, s->yres); + s->lock = false; +} + +static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835FBState *s = opaque; + uint32_t res = 0; + + switch (offset) { + case MBOX_AS_DATA: + res = MBOX_CHAN_FB; + s->pending = false; + qemu_set_irq(s->mbox_irq, 0); + break; + + case MBOX_AS_PENDING: + res = s->pending; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + + return res; +} + +static void bcm2835_fb_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835FBState *s = opaque; + + switch (offset) { + case MBOX_AS_DATA: + /* bcm2835_mbox should check our pending status before pushing */ + assert(!s->pending); + s->pending = true; + bcm2835_fb_mbox_push(s, value); + qemu_set_irq(s->mbox_irq, 1); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return; + } +} + +static const MemoryRegionOps bcm2835_fb_ops = { + .read = bcm2835_fb_read, + .write = bcm2835_fb_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_fb = { + .name = TYPE_BCM2835_FB, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(lock, BCM2835FBState), + VMSTATE_BOOL(invalidate, BCM2835FBState), + VMSTATE_BOOL(pending, BCM2835FBState), + VMSTATE_UINT32(xres, BCM2835FBState), + VMSTATE_UINT32(yres, BCM2835FBState), + VMSTATE_UINT32(xres_virtual, BCM2835FBState), + VMSTATE_UINT32(yres_virtual, BCM2835FBState), + VMSTATE_UINT32(xoffset, BCM2835FBState), + VMSTATE_UINT32(yoffset, BCM2835FBState), + VMSTATE_UINT32(bpp, BCM2835FBState), + VMSTATE_UINT32(base, BCM2835FBState), + VMSTATE_UINT32(pitch, BCM2835FBState), + VMSTATE_UINT32(size, BCM2835FBState), + VMSTATE_UINT32(pixo, BCM2835FBState), + VMSTATE_UINT32(alpha, BCM2835FBState), + VMSTATE_END_OF_LIST() + } +}; + +static const GraphicHwOps vgafb_ops = { + .invalidate = fb_invalidate_display, + .gfx_update = fb_update_display, +}; + +static void bcm2835_fb_init(Object *obj) +{ + BCM2835FBState *s = BCM2835_FB(obj); + + memory_region_init_io(&s->iomem, obj, &bcm2835_fb_ops, s, TYPE_BCM2835_FB, + 0x10); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq); +} + +static void bcm2835_fb_reset(DeviceState *dev) +{ + BCM2835FBState *s = BCM2835_FB(dev); + + s->pending = false; + + s->xres_virtual = s->xres; + s->yres_virtual = s->yres; + s->xoffset = 0; + s->yoffset = 0; + s->base = s->vcram_base + BCM2835_FB_OFFSET; + s->pitch = s->xres * (s->bpp >> 3); + s->size = s->yres * s->pitch; + + s->invalidate = true; + s->lock = false; +} + +static void bcm2835_fb_realize(DeviceState *dev, Error **errp) +{ + BCM2835FBState *s = BCM2835_FB(dev); + Error *err = NULL; + Object *obj; + + if (s->vcram_base == 0) { + error_setg(errp, "%s: required vcram-base property not set", __func__); + return; + } + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + bcm2835_fb_reset(dev); + + s->con = graphic_console_init(dev, 0, &vgafb_ops, s); + qemu_console_resize(s->con, s->xres, s->yres); +} + +static Property bcm2835_fb_props[] = { + DEFINE_PROP_UINT32("vcram-base", BCM2835FBState, vcram_base, 0),/*required*/ + DEFINE_PROP_UINT32("vcram-size", BCM2835FBState, vcram_size, + DEFAULT_VCRAM_SIZE), + DEFINE_PROP_UINT32("xres", BCM2835FBState, xres, 640), + DEFINE_PROP_UINT32("yres", BCM2835FBState, yres, 480), + DEFINE_PROP_UINT32("bpp", BCM2835FBState, bpp, 16), + DEFINE_PROP_UINT32("pixo", BCM2835FBState, pixo, 1), /* 1=RGB, 0=BGR */ + DEFINE_PROP_UINT32("alpha", BCM2835FBState, alpha, 2), /* alpha ignored */ + DEFINE_PROP_END_OF_LIST() +}; + +static void bcm2835_fb_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = bcm2835_fb_props; + dc->realize = bcm2835_fb_realize; + dc->reset = bcm2835_fb_reset; + dc->vmsd = &vmstate_bcm2835_fb; +} + +static TypeInfo bcm2835_fb_info = { + .name = TYPE_BCM2835_FB, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835FBState), + .class_init = bcm2835_fb_class_init, + .instance_init = bcm2835_fb_init, +}; + +static void bcm2835_fb_register_types(void) +{ + type_register_static(&bcm2835_fb_info); +} + +type_init(bcm2835_fb_register_types) diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 889adf56b3..e19d360e5a 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -15,6 +15,7 @@ #include "exec/address-spaces.h" #include "hw/sysbus.h" #include "hw/char/bcm2835_aux.h" +#include "hw/display/bcm2835_fb.h" #include "hw/intc/bcm2835_ic.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_mbox.h" @@ -35,6 +36,7 @@ typedef struct BCM2835PeripheralState { SysBusDevice *uart0; BCM2835AuxState aux; + BCM2835FBState fb; BCM2835ICState ic; BCM2835PropertyState property; BCM2835MboxState mboxes; diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h new file mode 100644 index 0000000000..9a12d7afa2 --- /dev/null +++ b/include/hw/display/bcm2835_fb.h @@ -0,0 +1,47 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous + * + * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft + * Written by Andrew Baumann + * + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_FB_H +#define BCM2835_FB_H + +#include "hw/sysbus.h" +#include "exec/address-spaces.h" +#include "ui/console.h" + +#define TYPE_BCM2835_FB "bcm2835-fb" +#define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB) + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + uint32_t vcram_base, vcram_size; + MemoryRegion *dma_mr; + AddressSpace dma_as; + MemoryRegion iomem; + MemoryRegionSection fbsection; + QemuConsole *con; + qemu_irq mbox_irq; + + bool lock, invalidate, pending; + uint32_t xres, yres; + uint32_t xres_virtual, yres_virtual; + uint32_t xoffset, yoffset; + uint32_t bpp; + uint32_t base, pitch, size; + uint32_t pixo, alpha; +} BCM2835FBState; + +void bcm2835_fb_reconfigure(BCM2835FBState *s, uint32_t *xres, uint32_t *yres, + uint32_t *xoffset, uint32_t *yoffset, uint32_t *bpp, + uint32_t *pixo, uint32_t *alpha); + +#endif -- cgit 1.4.1 From 355a8ccc5c57b7ce8758585e2d5d8b7f82b38bec Mon Sep 17 00:00:00 2001 From: Grégory ESTRADE Date: Wed, 16 Mar 2016 17:06:01 +0000 Subject: bcm2835_property: implement framebuffer control/configuration properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The property channel driver now interfaces with the framebuffer device to query and set framebuffer parameters. As a result of this, the "get ARM RAM size" query now correctly returns the video RAM base address (not total RAM size), and the ram-size property is no longer relevant here. Signed-off-by: Grégory ESTRADE Reviewed-by: Peter Maydell Signed-off-by: Andrew Baumann Message-id: 1457467526-8840-5-git-send-email-Andrew.Baumann@microsoft.com [AB: cleanup/refactoring for upstream submission] Signed-off-by: Andrew Baumann Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 8 +-- hw/arm/raspi.c | 7 +- hw/misc/bcm2835_property.c | 139 ++++++++++++++++++++++++++++++++++++- include/hw/misc/bcm2835_property.h | 5 +- 4 files changed, 144 insertions(+), 15 deletions(-) (limited to 'hw/arm/bcm2835_peripherals.c') diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index c2fe6b7625..4d74a18110 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -79,6 +79,8 @@ static void bcm2835_peripherals_init(Object *obj) "board-rev", &error_abort); qdev_set_parent_bus(DEVICE(&s->property), sysbus_get_default()); + object_property_add_const_link(OBJECT(&s->property), "fb", + OBJECT(&s->fb), &error_abort); object_property_add_const_link(OBJECT(&s->property), "dma-mr", OBJECT(&s->gpu_bus_mr), &error_abort); @@ -211,12 +213,6 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB)); /* Property channel */ - object_property_set_int(OBJECT(&s->property), ram_size, "ram-size", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->property), true, "realized", &err); if (err) { error_propagate(errp, err); diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 54982095b2..83fe8097e5 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -164,11 +164,6 @@ static void raspi2_machine_init(MachineClass *mc) mc->no_floppy = 1; mc->no_cdrom = 1; mc->max_cpus = BCM2836_NCPUS; - - /* XXX: Temporary restriction in RAM size from the full 1GB. Since - * we do not yet support the framebuffer / GPU, we need to limit - * RAM usable by the OS to sit below the peripherals. - */ - mc->default_ram_size = 0x3F000000; /* BCM2836_PERI_BASE */ + mc->default_ram_size = 1024 * 1024 * 1024; }; DEFINE_MACHINE("raspi2", raspi2_machine_init) diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 41fbbe3e7f..15dcc02f99 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -17,6 +17,11 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tot_len; size_t resplen; uint32_t tmp; + int n; + uint32_t offset, length, color; + uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; + uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, + *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; value &= ~0xf; @@ -60,7 +65,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* base */ stl_le_phys(&s->dma_as, value + 12, 0); /* size */ - stl_le_phys(&s->dma_as, value + 16, s->ram_size); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base); + resplen = 8; + break; + case 0x00010006: /* Get VC memory */ + /* base */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base); + /* size */ + stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size); resplen = 8; break; case 0x00028001: /* Set power state */ @@ -122,6 +134,114 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) resplen = 8; break; + /* Frame buffer */ + + case 0x00040001: /* Allocate buffer */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->size); + resplen = 8; + break; + case 0x00048001: /* Release buffer */ + resplen = 0; + break; + case 0x00040002: /* Blank screen */ + resplen = 4; + break; + case 0x00040003: /* Get display width/height */ + case 0x00040004: + stl_le_phys(&s->dma_as, value + 12, s->fbdev->xres); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->yres); + resplen = 8; + break; + case 0x00044003: /* Test display width/height */ + case 0x00044004: + resplen = 8; + break; + case 0x00048003: /* Set display width/height */ + case 0x00048004: + xres = ldl_le_phys(&s->dma_as, value + 12); + newxres = &xres; + yres = ldl_le_phys(&s->dma_as, value + 16); + newyres = &yres; + resplen = 8; + break; + case 0x00040005: /* Get depth */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->bpp); + resplen = 4; + break; + case 0x00044005: /* Test depth */ + resplen = 4; + break; + case 0x00048005: /* Set depth */ + bpp = ldl_le_phys(&s->dma_as, value + 12); + newbpp = &bpp; + resplen = 4; + break; + case 0x00040006: /* Get pixel order */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->pixo); + resplen = 4; + break; + case 0x00044006: /* Test pixel order */ + resplen = 4; + break; + case 0x00048006: /* Set pixel order */ + pixo = ldl_le_phys(&s->dma_as, value + 12); + newpixo = &pixo; + resplen = 4; + break; + case 0x00040007: /* Get alpha */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->alpha); + resplen = 4; + break; + case 0x00044007: /* Test pixel alpha */ + resplen = 4; + break; + case 0x00048007: /* Set alpha */ + alpha = ldl_le_phys(&s->dma_as, value + 12); + newalpha = α + resplen = 4; + break; + case 0x00040008: /* Get pitch */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->pitch); + resplen = 4; + break; + case 0x00040009: /* Get virtual offset */ + stl_le_phys(&s->dma_as, value + 12, s->fbdev->xoffset); + stl_le_phys(&s->dma_as, value + 16, s->fbdev->yoffset); + resplen = 8; + break; + case 0x00044009: /* Test virtual offset */ + resplen = 8; + break; + case 0x00048009: /* Set virtual offset */ + xoffset = ldl_le_phys(&s->dma_as, value + 12); + newxoffset = &xoffset; + yoffset = ldl_le_phys(&s->dma_as, value + 16); + newyoffset = &yoffset; + resplen = 8; + break; + case 0x0004000a: /* Get/Test/Set overscan */ + case 0x0004400a: + case 0x0004800a: + stl_le_phys(&s->dma_as, value + 12, 0); + stl_le_phys(&s->dma_as, value + 16, 0); + stl_le_phys(&s->dma_as, value + 20, 0); + stl_le_phys(&s->dma_as, value + 24, 0); + resplen = 16; + break; + case 0x0004800b: /* Set palette */ + offset = ldl_le_phys(&s->dma_as, value + 12); + length = ldl_le_phys(&s->dma_as, value + 16); + n = 0; + while (n < length - offset) { + color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2)); + stl_le_phys(&s->dma_as, + s->fbdev->vcram_base + ((offset + n) << 2), color); + n++; + } + stl_le_phys(&s->dma_as, value + 12, 0); + resplen = 4; + break; case 0x00060001: /* Get DMA channels */ /* channels 2-5 */ @@ -147,6 +267,13 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) value += bufsize + 12; } + /* Reconfigure framebuffer if required */ + if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo + || newalpha) { + bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, + newyoffset, newbpp, newpixo, newalpha); + } + /* Buffer response code */ stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31)); } @@ -241,6 +368,15 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) Object *obj; Error *err = NULL; + obj = object_property_get_link(OBJECT(dev), "fb", &err); + if (obj == NULL) { + error_setg(errp, "%s: required fb link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->fbdev = BCM2835_FB(obj); + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); if (obj == NULL) { error_setg(errp, "%s: required dma-mr link not found: %s", @@ -259,7 +395,6 @@ static void bcm2835_property_realize(DeviceState *dev, Error **errp) static Property bcm2835_property_props[] = { DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0), - DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0), DEFINE_PROP_END_OF_LIST() }; diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index df889eaa08..edcab603ce 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -9,6 +9,7 @@ #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "net/net.h" +#include "hw/display/bcm2835_fb.h" #define TYPE_BCM2835_PROPERTY "bcm2835-property" #define BCM2835_PROPERTY(obj) \ @@ -18,13 +19,15 @@ typedef struct { /*< private >*/ SysBusDevice busdev; /*< public >*/ + MemoryRegion *dma_mr; AddressSpace dma_as; MemoryRegion iomem; qemu_irq mbox_irq; + BCM2835FBState *fbdev; + MACAddr macaddr; uint32_t board_rev; - uint32_t ram_size; uint32_t addr; bool pending; } BCM2835PropertyState; -- cgit 1.4.1 From 6717f587a478be37294cc5cfbbd84c5a6ce1aa1f Mon Sep 17 00:00:00 2001 From: Grégory ESTRADE Date: Wed, 16 Mar 2016 17:06:02 +0000 Subject: bcm2835_dma: add emulation of Raspberry Pi DMA controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At present, all DMA transfers complete inline (so a looping descriptor queue will lock up the device). We also do not model pause/abort, arbitrarion/priority, or debug features. Signed-off-by: Grégory ESTRADE Reviewed-by: Peter Maydell Signed-off-by: Andrew Baumann Message-id: 1457467526-8840-6-git-send-email-Andrew.Baumann@microsoft.com [AB: implement 2D mode, cleanup/refactoring for upstream submission] Signed-off-by: Andrew Baumann Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/bcm2835_peripherals.c | 26 +++ hw/dma/Makefile.objs | 1 + hw/dma/bcm2835_dma.c | 408 +++++++++++++++++++++++++++++++++++ include/hw/arm/bcm2835_peripherals.h | 2 + include/hw/dma/bcm2835_dma.h | 47 ++++ 5 files changed, 484 insertions(+) create mode 100644 hw/dma/bcm2835_dma.c create mode 100644 include/hw/dma/bcm2835_dma.h (limited to 'hw/arm/bcm2835_peripherals.c') diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 4d74a18110..8099a8a92e 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -88,6 +88,14 @@ static void bcm2835_peripherals_init(Object *obj) object_initialize(&s->sdhci, sizeof(s->sdhci), TYPE_SYSBUS_SDHCI); object_property_add_child(obj, "sdhci", OBJECT(&s->sdhci), NULL); qdev_set_parent_bus(DEVICE(&s->sdhci), sysbus_get_default()); + + /* DMA Channels */ + object_initialize(&s->dma, sizeof(s->dma), TYPE_BCM2835_DMA); + object_property_add_child(obj, "dma", OBJECT(&s->dma), NULL); + qdev_set_parent_bus(DEVICE(&s->dma), sysbus_get_default()); + + object_property_add_const_link(OBJECT(&s->dma), "dma-mr", + OBJECT(&s->gpu_bus_mr), &error_abort); } static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) @@ -258,6 +266,24 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) return; } + /* DMA Channels */ + object_property_set_bool(OBJECT(&s->dma), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(&s->peri_mr, DMA_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0)); + memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1)); + + for (n = 0; n <= 12; n++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n, + qdev_get_gpio_in_named(DEVICE(&s->ic), + BCM2835_IC_GPU_IRQ, + INTERRUPT_DMA0 + n)); + } } static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index 0e65ed0d74..a1abbcf746 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -11,3 +11,4 @@ common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o +obj-$(CONFIG_RASPI) += bcm2835_dma.o diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c new file mode 100644 index 0000000000..c7ce4e4881 --- /dev/null +++ b/hw/dma/bcm2835_dma.c @@ -0,0 +1,408 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#include "qemu/osdep.h" +#include "hw/dma/bcm2835_dma.h" + +/* DMA CS Control and Status bits */ +#define BCM2708_DMA_ACTIVE (1 << 0) +#define BCM2708_DMA_END (1 << 1) /* GE */ +#define BCM2708_DMA_INT (1 << 2) +#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ +#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ +#define BCM2708_DMA_ERR (1 << 8) +#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ +#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ + +/* DMA control block "info" field bits */ +#define BCM2708_DMA_INT_EN (1 << 0) +#define BCM2708_DMA_TDMODE (1 << 1) +#define BCM2708_DMA_WAIT_RESP (1 << 3) +#define BCM2708_DMA_D_INC (1 << 4) +#define BCM2708_DMA_D_WIDTH (1 << 5) +#define BCM2708_DMA_D_DREQ (1 << 6) +#define BCM2708_DMA_D_IGNORE (1 << 7) +#define BCM2708_DMA_S_INC (1 << 8) +#define BCM2708_DMA_S_WIDTH (1 << 9) +#define BCM2708_DMA_S_DREQ (1 << 10) +#define BCM2708_DMA_S_IGNORE (1 << 11) + +/* Register offsets */ +#define BCM2708_DMA_CS 0x00 /* Control and Status */ +#define BCM2708_DMA_ADDR 0x04 /* Control block address */ +/* the current control block appears in the following registers - read only */ +#define BCM2708_DMA_INFO 0x08 +#define BCM2708_DMA_SOURCE_AD 0x0c +#define BCM2708_DMA_DEST_AD 0x10 +#define BCM2708_DMA_TXFR_LEN 0x14 +#define BCM2708_DMA_STRIDE 0x18 +#define BCM2708_DMA_NEXTCB 0x1C +#define BCM2708_DMA_DEBUG 0x20 + +#define BCM2708_DMA_INT_STATUS 0xfe0 /* Interrupt status of each channel */ +#define BCM2708_DMA_ENABLE 0xff0 /* Global enable bits for each channel */ + +#define BCM2708_DMA_CS_RW_MASK 0x30ff0001 /* All RW bits in DMA_CS */ + +static void bcm2835_dma_update(BCM2835DMAState *s, unsigned c) +{ + BCM2835DMAChan *ch = &s->chan[c]; + uint32_t data, xlen, ylen; + int16_t dst_stride, src_stride; + + if (!(s->enable & (1 << c))) { + return; + } + + while ((s->enable & (1 << c)) && (ch->conblk_ad != 0)) { + /* CB fetch */ + ch->ti = ldl_le_phys(&s->dma_as, ch->conblk_ad); + ch->source_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 4); + ch->dest_ad = ldl_le_phys(&s->dma_as, ch->conblk_ad + 8); + ch->txfr_len = ldl_le_phys(&s->dma_as, ch->conblk_ad + 12); + ch->stride = ldl_le_phys(&s->dma_as, ch->conblk_ad + 16); + ch->nextconbk = ldl_le_phys(&s->dma_as, ch->conblk_ad + 20); + + if (ch->ti & BCM2708_DMA_TDMODE) { + /* 2D transfer mode */ + ylen = (ch->txfr_len >> 16) & 0x3fff; + xlen = ch->txfr_len & 0xffff; + dst_stride = ch->stride >> 16; + src_stride = ch->stride & 0xffff; + } else { + ylen = 1; + xlen = ch->txfr_len; + dst_stride = 0; + src_stride = 0; + } + + while (ylen != 0) { + /* Normal transfer mode */ + while (xlen != 0) { + if (ch->ti & BCM2708_DMA_S_IGNORE) { + /* Ignore reads */ + data = 0; + } else { + data = ldl_le_phys(&s->dma_as, ch->source_ad); + } + if (ch->ti & BCM2708_DMA_S_INC) { + ch->source_ad += 4; + } + + if (ch->ti & BCM2708_DMA_D_IGNORE) { + /* Ignore writes */ + } else { + stl_le_phys(&s->dma_as, ch->dest_ad, data); + } + if (ch->ti & BCM2708_DMA_D_INC) { + ch->dest_ad += 4; + } + + /* update remaining transfer length */ + xlen -= 4; + if (ch->ti & BCM2708_DMA_TDMODE) { + ch->txfr_len = (ylen << 16) | xlen; + } else { + ch->txfr_len = xlen; + } + } + + if (--ylen != 0) { + ch->source_ad += src_stride; + ch->dest_ad += dst_stride; + } + } + ch->cs |= BCM2708_DMA_END; + if (ch->ti & BCM2708_DMA_INT_EN) { + ch->cs |= BCM2708_DMA_INT; + s->int_status |= (1 << c); + qemu_set_irq(ch->irq, 1); + } + + /* Process next CB */ + ch->conblk_ad = ch->nextconbk; + } + + ch->cs &= ~BCM2708_DMA_ACTIVE; + ch->cs |= BCM2708_DMA_ISPAUSED; +} + +static void bcm2835_dma_chan_reset(BCM2835DMAChan *ch) +{ + ch->cs = 0; + ch->conblk_ad = 0; +} + +static uint64_t bcm2835_dma_read(BCM2835DMAState *s, hwaddr offset, + unsigned size, unsigned c) +{ + BCM2835DMAChan *ch; + uint32_t res = 0; + + assert(size == 4); + assert(c < BCM2835_DMA_NCHANS); + + ch = &s->chan[c]; + + switch (offset) { + case BCM2708_DMA_CS: + res = ch->cs; + break; + case BCM2708_DMA_ADDR: + res = ch->conblk_ad; + break; + case BCM2708_DMA_INFO: + res = ch->ti; + break; + case BCM2708_DMA_SOURCE_AD: + res = ch->source_ad; + break; + case BCM2708_DMA_DEST_AD: + res = ch->dest_ad; + break; + case BCM2708_DMA_TXFR_LEN: + res = ch->txfr_len; + break; + case BCM2708_DMA_STRIDE: + res = ch->stride; + break; + case BCM2708_DMA_NEXTCB: + res = ch->nextconbk; + break; + case BCM2708_DMA_DEBUG: + res = ch->debug; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } + return res; +} + +static void bcm2835_dma_write(BCM2835DMAState *s, hwaddr offset, + uint64_t value, unsigned size, unsigned c) +{ + BCM2835DMAChan *ch; + uint32_t oldcs; + + assert(size == 4); + assert(c < BCM2835_DMA_NCHANS); + + ch = &s->chan[c]; + + switch (offset) { + case BCM2708_DMA_CS: + oldcs = ch->cs; + if (value & BCM2708_DMA_RESET) { + bcm2835_dma_chan_reset(ch); + } + if (value & BCM2708_DMA_ABORT) { + /* abort is a no-op, since we always run to completion */ + } + if (value & BCM2708_DMA_END) { + ch->cs &= ~BCM2708_DMA_END; + } + if (value & BCM2708_DMA_INT) { + ch->cs &= ~BCM2708_DMA_INT; + s->int_status &= ~(1 << c); + qemu_set_irq(ch->irq, 0); + } + ch->cs &= ~BCM2708_DMA_CS_RW_MASK; + ch->cs |= (value & BCM2708_DMA_CS_RW_MASK); + if (!(oldcs & BCM2708_DMA_ACTIVE) && (ch->cs & BCM2708_DMA_ACTIVE)) { + bcm2835_dma_update(s, c); + } + break; + case BCM2708_DMA_ADDR: + ch->conblk_ad = value; + break; + case BCM2708_DMA_DEBUG: + ch->debug = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + break; + } +} + +static uint64_t bcm2835_dma0_read(void *opaque, hwaddr offset, unsigned size) +{ + BCM2835DMAState *s = opaque; + + if (offset < 0xf00) { + return bcm2835_dma_read(s, (offset & 0xff), size, (offset >> 8) & 0xf); + } else { + switch (offset) { + case BCM2708_DMA_INT_STATUS: + return s->int_status; + case BCM2708_DMA_ENABLE: + return s->enable; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + return 0; + } + } +} + +static uint64_t bcm2835_dma15_read(void *opaque, hwaddr offset, unsigned size) +{ + return bcm2835_dma_read(opaque, (offset & 0xff), size, 15); +} + +static void bcm2835_dma0_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + BCM2835DMAState *s = opaque; + + if (offset < 0xf00) { + bcm2835_dma_write(s, (offset & 0xff), value, size, (offset >> 8) & 0xf); + } else { + switch (offset) { + case BCM2708_DMA_INT_STATUS: + break; + case BCM2708_DMA_ENABLE: + s->enable = (value & 0xffff); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n", + __func__, offset); + } + } + +} + +static void bcm2835_dma15_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + bcm2835_dma_write(opaque, (offset & 0xff), value, size, 15); +} + +static const MemoryRegionOps bcm2835_dma0_ops = { + .read = bcm2835_dma0_read, + .write = bcm2835_dma0_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const MemoryRegionOps bcm2835_dma15_ops = { + .read = bcm2835_dma15_read, + .write = bcm2835_dma15_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_bcm2835_dma_chan = { + .name = TYPE_BCM2835_DMA "-chan", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cs, BCM2835DMAChan), + VMSTATE_UINT32(conblk_ad, BCM2835DMAChan), + VMSTATE_UINT32(ti, BCM2835DMAChan), + VMSTATE_UINT32(source_ad, BCM2835DMAChan), + VMSTATE_UINT32(dest_ad, BCM2835DMAChan), + VMSTATE_UINT32(txfr_len, BCM2835DMAChan), + VMSTATE_UINT32(stride, BCM2835DMAChan), + VMSTATE_UINT32(nextconbk, BCM2835DMAChan), + VMSTATE_UINT32(debug, BCM2835DMAChan), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_bcm2835_dma = { + .name = TYPE_BCM2835_DMA, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(chan, BCM2835DMAState, BCM2835_DMA_NCHANS, 1, + vmstate_bcm2835_dma_chan, BCM2835DMAChan), + VMSTATE_UINT32(int_status, BCM2835DMAState), + VMSTATE_UINT32(enable, BCM2835DMAState), + VMSTATE_END_OF_LIST() + } +}; + +static void bcm2835_dma_init(Object *obj) +{ + BCM2835DMAState *s = BCM2835_DMA(obj); + int n; + + /* DMA channels 0-14 occupy a contiguous block of IO memory, along + * with the global enable and interrupt status bits. Channel 15 + * has the same register map, but is mapped at a discontiguous + * address in a separate IO block. + */ + memory_region_init_io(&s->iomem0, OBJECT(s), &bcm2835_dma0_ops, s, + TYPE_BCM2835_DMA, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem0); + + memory_region_init_io(&s->iomem15, OBJECT(s), &bcm2835_dma15_ops, s, + TYPE_BCM2835_DMA "-chan15", 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem15); + + for (n = 0; n < 16; n++) { + sysbus_init_irq(SYS_BUS_DEVICE(s), &s->chan[n].irq); + } +} + +static void bcm2835_dma_reset(DeviceState *dev) +{ + BCM2835DMAState *s = BCM2835_DMA(dev); + int n; + + s->enable = 0xffff; + s->int_status = 0; + for (n = 0; n < BCM2835_DMA_NCHANS; n++) { + bcm2835_dma_chan_reset(&s->chan[n]); + } +} + +static void bcm2835_dma_realize(DeviceState *dev, Error **errp) +{ + BCM2835DMAState *s = BCM2835_DMA(dev); + Error *err = NULL; + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "dma-mr", &err); + if (obj == NULL) { + error_setg(errp, "%s: required dma-mr link not found: %s", + __func__, error_get_pretty(err)); + return; + } + + s->dma_mr = MEMORY_REGION(obj); + address_space_init(&s->dma_as, s->dma_mr, NULL); + + bcm2835_dma_reset(dev); +} + +static void bcm2835_dma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = bcm2835_dma_realize; + dc->reset = bcm2835_dma_reset; + dc->vmsd = &vmstate_bcm2835_dma; +} + +static TypeInfo bcm2835_dma_info = { + .name = TYPE_BCM2835_DMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(BCM2835DMAState), + .class_init = bcm2835_dma_class_init, + .instance_init = bcm2835_dma_init, +}; + +static void bcm2835_dma_register_types(void) +{ + type_register_static(&bcm2835_dma_info); +} + +type_init(bcm2835_dma_register_types) diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index e19d360e5a..e12ae3721a 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -16,6 +16,7 @@ #include "hw/sysbus.h" #include "hw/char/bcm2835_aux.h" #include "hw/display/bcm2835_fb.h" +#include "hw/dma/bcm2835_dma.h" #include "hw/intc/bcm2835_ic.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_mbox.h" @@ -37,6 +38,7 @@ typedef struct BCM2835PeripheralState { SysBusDevice *uart0; BCM2835AuxState aux; BCM2835FBState fb; + BCM2835DMAState dma; BCM2835ICState ic; BCM2835PropertyState property; BCM2835MboxState mboxes; diff --git a/include/hw/dma/bcm2835_dma.h b/include/hw/dma/bcm2835_dma.h new file mode 100644 index 0000000000..75312e2e17 --- /dev/null +++ b/include/hw/dma/bcm2835_dma.h @@ -0,0 +1,47 @@ +/* + * Raspberry Pi emulation (c) 2012 Gregory Estrade + * This code is licensed under the GNU GPLv2 and later. + */ + +#ifndef BCM2835_DMA_H +#define BCM2835_DMA_H + +#include "qemu-common.h" +#include "exec/address-spaces.h" +#include "hw/sysbus.h" + +typedef struct { + uint32_t cs; + uint32_t conblk_ad; + uint32_t ti; + uint32_t source_ad; + uint32_t dest_ad; + uint32_t txfr_len; + uint32_t stride; + uint32_t nextconbk; + uint32_t debug; + + qemu_irq irq; +} BCM2835DMAChan; + +#define TYPE_BCM2835_DMA "bcm2835-dma" +#define BCM2835_DMA(obj) \ + OBJECT_CHECK(BCM2835DMAState, (obj), TYPE_BCM2835_DMA) + +#define BCM2835_DMA_NCHANS 16 + +typedef struct { + /*< private >*/ + SysBusDevice busdev; + /*< public >*/ + + MemoryRegion iomem0, iomem15; + MemoryRegion *dma_mr; + AddressSpace dma_as; + + BCM2835DMAChan chan[BCM2835_DMA_NCHANS]; + uint32_t int_status; + uint32_t enable; +} BCM2835DMAState; + +#endif -- cgit 1.4.1