aboutsummaryrefslogtreecommitdiff
path: root/hw/display
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2018-01-25 11:45:30 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-01-25 11:45:30 +0000
commit24da047af0e99a83fcc0d50b86c0f2627f7418b3 (patch)
treed90727b6edeee71497ace8dc91b3476a30a98b65 /hw/display
parentfbe5dac7b2777626670097218ce4dfc4a19a17e9 (diff)
pl110: Implement vertical compare/next base interrupts
This implements rudimentary support for interrupt generation on the PL110. I am working on a new DRI/KMS driver for Linux and since that uses the blanking interrupt, we need something to fire here. Without any interrupt support Linux waits for a while and then gives ugly messages about the vblank not working in the console (it does not hang perpetually or anything though, DRI is pretty forgiving). I solved it for now by setting up a timer to fire at 60Hz and pull the interrupts for "vertical compare" and "next memory base" at this interval. This works fine and fires roughly the same number of IRQs on QEMU as on the hardware and leaves the console clean and nice. People who want to create more accurate emulation can probably work on top of this if need be. It is certainly closer to the hardware behaviour than what we have today anyway. Cc: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Message-id: 20180123225654.5764-1-linus.walleij@linaro.org Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: folded long lines] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/display')
-rw-r--r--hw/display/pl110.c30
1 files changed, 29 insertions, 1 deletions
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index 8c7dcc6f0a..cf68457fd1 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -12,6 +12,7 @@
#include "ui/console.h"
#include "framebuffer.h"
#include "ui/pixel_ops.h"
+#include "qemu/timer.h"
#include "qemu/log.h"
#define PL110_CR_EN 0x001
@@ -19,6 +20,8 @@
#define PL110_CR_BEBO 0x200
#define PL110_CR_BEPO 0x400
#define PL110_CR_PWR 0x800
+#define PL110_IE_NB 0x004
+#define PL110_IE_VC 0x008
enum pl110_bppmode
{
@@ -50,6 +53,7 @@ typedef struct PL110State {
MemoryRegion iomem;
MemoryRegionSection fbsection;
QemuConsole *con;
+ QEMUTimer *vblank_timer;
int version;
uint32_t timing[4];
@@ -320,7 +324,24 @@ static void pl110_resize(PL110State *s, int width, int height)
/* Update interrupts. */
static void pl110_update(PL110State *s)
{
- /* TODO: Implement interrupts. */
+ /* Raise IRQ if enabled and any status bit is 1 */
+ if (s->int_status & s->int_mask) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void pl110_vblank_interrupt(void *opaque)
+{
+ PL110State *s = opaque;
+
+ /* Fire the vertical compare and next base IRQs and re-arm */
+ s->int_status |= (PL110_IE_NB | PL110_IE_VC);
+ timer_mod(s->vblank_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ NANOSECONDS_PER_SECOND / 60);
+ pl110_update(s);
}
static uint64_t pl110_read(void *opaque, hwaddr offset,
@@ -429,6 +450,11 @@ static void pl110_write(void *opaque, hwaddr offset,
s->bpp = (val >> 1) & 7;
if (pl110_enabled(s)) {
qemu_console_resize(s->con, s->cols, s->rows);
+ timer_mod(s->vblank_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ NANOSECONDS_PER_SECOND / 60);
+ } else {
+ timer_del(s->vblank_timer);
}
break;
case 10: /* LCDICR */
@@ -474,6 +500,8 @@ static void pl110_realize(DeviceState *dev, Error **errp)
memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
+ s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+ pl110_vblank_interrupt, s);
qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
}