The MacOS driver expects a 60.15Hz vertical blank interrupt to be generated by
the framebuffer which in turn schedules the mouse driver via the Vertical Retrace
Manager.
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
---
hw/display/macfb.c | 81 ++++++++++++++++++++++++++++++++++++++
include/hw/display/macfb.h | 8 ++++
2 files changed, 89 insertions(+)
diff --git a/hw/display/macfb.c b/hw/display/macfb.c
index 29f6ad8eba..60a203e67b 100644
--- a/hw/display/macfb.c
+++ b/hw/display/macfb.c
@@ -33,9 +33,16 @@
#define DAFB_MODE_CTRL1 0x8
#define DAFB_MODE_CTRL2 0xc
#define DAFB_MODE_SENSE 0x1c
+#define DAFB_INTR_MASK 0x104
+#define DAFB_INTR_STAT 0x108
+#define DAFB_INTR_CLEAR 0x10c
#define DAFB_RESET 0x200
#define DAFB_LUT 0x213
+#define DAFB_INTR_VBL 0x4
+
+/* Vertical Blank period (60.15Hz) */
+#define DAFB_INTR_VBL_PERIOD_NS 16625800
/*
* Quadra sense codes taken from Apple Technical Note HW26:
@@ -449,6 +456,32 @@ static void macfb_update_display(void *opaque)
macfb_draw_graphic(s);
}
+static void macfb_update_irq(MacfbState *s)
+{
+ uint32_t irq_state = s->irq_state & s->irq_mask;
+
+ if (irq_state) {
+ qemu_irq_raise(s->irq);
+ } else {
+ qemu_irq_lower(s->irq);
+ }
+}
+
+static void macfb_vbl_timer(void *opaque)
+{
+ MacfbState *s = opaque;
+ int64_t next_vbl;
+
+ s->irq_state |= DAFB_INTR_VBL;
+ macfb_update_irq(s);
+
+ /* 60 Hz irq */
+ next_vbl = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ DAFB_INTR_VBL_PERIOD_NS) /
+ DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS;
+ timer_mod(s->vbl_timer, next_vbl);
+}
+
static void macfb_reset(MacfbState *s)
{
int i;
@@ -477,6 +510,9 @@ static uint64_t macfb_ctrl_read(void *opaque,
case DAFB_MODE_CTRL2:
val = s->regs[addr >> 2];
break;
+ case DAFB_INTR_STAT:
+ val = s->irq_state;
+ break;
case DAFB_MODE_SENSE:
val = macfb_sense_read(s);
break;
@@ -492,6 +528,8 @@ static void macfb_ctrl_write(void *opaque,
unsigned int size)
{
MacfbState *s = opaque;
+ int64_t next_vbl;
+
switch (addr) {
case DAFB_MODE_VADDR1:
case DAFB_MODE_VADDR2:
@@ -507,8 +545,25 @@ static void macfb_ctrl_write(void *opaque,
case DAFB_MODE_SENSE:
macfb_sense_write(s, val);
break;
+ case DAFB_INTR_MASK:
+ s->irq_mask = val;
+ if (val & DAFB_INTR_VBL) {
+ next_vbl = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
+ DAFB_INTR_VBL_PERIOD_NS) /
+ DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS;
+ timer_mod(s->vbl_timer, next_vbl);
+ } else {
+ timer_del(s->vbl_timer);
+ }
+ break;
+ case DAFB_INTR_CLEAR:
+ s->irq_state &= ~DAFB_INTR_VBL;
+ macfb_update_irq(s);
+ break;
case DAFB_RESET:
s->palette_current = 0;
+ s->irq_state &= ~DAFB_INTR_VBL;
+ macfb_update_irq(s);
break;
case DAFB_LUT:
s->color_palette[s->palette_current++] = val;
@@ -586,6 +641,7 @@ static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp)
s->vram_bit_mask = MACFB_VRAM_SIZE - 1;
memory_region_set_coalescing(&s->mem_vram);
+ s->vbl_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, macfb_vbl_timer, s);
macfb_update_mode(s);
}
@@ -601,6 +657,16 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp)
sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram);
+
+ qdev_init_gpio_out(dev, &ms->irq, 1);
+}
+
+static void macfb_nubus_set_irq(void *opaque, int n, int level)
+{
+ MacfbNubusState *s = NUBUS_MACFB(opaque);
+ NubusDevice *nd = NUBUS_DEVICE(s);
+
+ nubus_set_irq(nd, level);
}
static void macfb_nubus_realize(DeviceState *dev, Error **errp)
@@ -622,6 +688,19 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp)
memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl);
memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram);
+
+ ms->irq = qemu_allocate_irq(macfb_nubus_set_irq, s, 0);
+}
+
+static void macfb_nubus_unrealize(DeviceState *dev)
+{
+ MacfbNubusState *s = NUBUS_MACFB(dev);
+ MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev);
+ MacfbState *ms = &s->macfb;
+
+ ndc->parent_unrealize(dev);
+
+ qemu_free_irq(ms->irq);
}
static void macfb_sysbus_reset(DeviceState *d)
@@ -672,6 +751,8 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data)
device_class_set_parent_realize(dc, macfb_nubus_realize,
&ndc->parent_realize);
+ device_class_set_parent_unrealize(dc, macfb_nubus_unrealize,
+ &ndc->parent_unrealize);
dc->desc = "Nubus Macintosh framebuffer";
dc->reset = macfb_nubus_reset;
dc->vmsd = &vmstate_macfb;
diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h
index 0aff0d84d2..e52775aa21 100644
--- a/include/hw/display/macfb.h
+++ b/include/hw/display/macfb.h
@@ -14,7 +14,9 @@
#define MACFB_H
#include "exec/memory.h"
+#include "hw/irq.h"
#include "ui/console.h"
+#include "qemu/timer.h"
#include "qom/object.h"
typedef enum {
@@ -63,6 +65,11 @@ typedef struct MacfbState {
uint32_t regs[MACFB_NUM_REGS];
MacFbMode *mode;
+
+ uint32_t irq_state;
+ uint32_t irq_mask;
+ QEMUTimer *vbl_timer;
+ qemu_irq irq;
} MacfbState;
#define TYPE_MACFB "sysbus-macfb"
@@ -81,6 +88,7 @@ struct MacfbNubusDeviceClass {
DeviceClass parent_class;
DeviceRealize parent_realize;
+ DeviceUnrealize parent_unrealize;
};
--
2.20.1
Le 02/10/2021 à 13:00, Mark Cave-Ayland a écrit :
> The MacOS driver expects a 60.15Hz vertical blank interrupt to be generated by
> the framebuffer which in turn schedules the mouse driver via the Vertical Retrace
> Manager.
>
> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> ---
> hw/display/macfb.c | 81 ++++++++++++++++++++++++++++++++++++++
> include/hw/display/macfb.h | 8 ++++
> 2 files changed, 89 insertions(+)
>
> diff --git a/hw/display/macfb.c b/hw/display/macfb.c
> index 29f6ad8eba..60a203e67b 100644
> --- a/hw/display/macfb.c
> +++ b/hw/display/macfb.c
> @@ -33,9 +33,16 @@
> #define DAFB_MODE_CTRL1 0x8
> #define DAFB_MODE_CTRL2 0xc
> #define DAFB_MODE_SENSE 0x1c
> +#define DAFB_INTR_MASK 0x104
> +#define DAFB_INTR_STAT 0x108
> +#define DAFB_INTR_CLEAR 0x10c
> #define DAFB_RESET 0x200
> #define DAFB_LUT 0x213
>
> +#define DAFB_INTR_VBL 0x4
> +
> +/* Vertical Blank period (60.15Hz) */
> +#define DAFB_INTR_VBL_PERIOD_NS 16625800
>
> /*
> * Quadra sense codes taken from Apple Technical Note HW26:
> @@ -449,6 +456,32 @@ static void macfb_update_display(void *opaque)
> macfb_draw_graphic(s);
> }
>
> +static void macfb_update_irq(MacfbState *s)
> +{
> + uint32_t irq_state = s->irq_state & s->irq_mask;
> +
> + if (irq_state) {
> + qemu_irq_raise(s->irq);
> + } else {
> + qemu_irq_lower(s->irq);
> + }
> +}
> +
> +static void macfb_vbl_timer(void *opaque)
> +{
> + MacfbState *s = opaque;
> + int64_t next_vbl;
> +
> + s->irq_state |= DAFB_INTR_VBL;
> + macfb_update_irq(s);
> +
> + /* 60 Hz irq */
> + next_vbl = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> + DAFB_INTR_VBL_PERIOD_NS) /
> + DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS;
> + timer_mod(s->vbl_timer, next_vbl);
perhaps you can move this to a function and call it here and below?
> +}
> +
> static void macfb_reset(MacfbState *s)
> {
> int i;
> @@ -477,6 +510,9 @@ static uint64_t macfb_ctrl_read(void *opaque,
> case DAFB_MODE_CTRL2:
> val = s->regs[addr >> 2];
> break;
> + case DAFB_INTR_STAT:
> + val = s->irq_state;
> + break;
> case DAFB_MODE_SENSE:
> val = macfb_sense_read(s);
> break;
> @@ -492,6 +528,8 @@ static void macfb_ctrl_write(void *opaque,
> unsigned int size)
> {
> MacfbState *s = opaque;
> + int64_t next_vbl;
> +
> switch (addr) {
> case DAFB_MODE_VADDR1:
> case DAFB_MODE_VADDR2:
> @@ -507,8 +545,25 @@ static void macfb_ctrl_write(void *opaque,
> case DAFB_MODE_SENSE:
> macfb_sense_write(s, val);
> break;
> + case DAFB_INTR_MASK:
> + s->irq_mask = val;
> + if (val & DAFB_INTR_VBL) {
> + next_vbl = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
> + DAFB_INTR_VBL_PERIOD_NS) /
> + DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS;
> + timer_mod(s->vbl_timer, next_vbl);
> + } else {
> + timer_del(s->vbl_timer);
> + }
> + break;
> + case DAFB_INTR_CLEAR:
> + s->irq_state &= ~DAFB_INTR_VBL;
> + macfb_update_irq(s);
> + break;
> case DAFB_RESET:
> s->palette_current = 0;
> + s->irq_state &= ~DAFB_INTR_VBL;
> + macfb_update_irq(s);
> break;
> case DAFB_LUT:
> s->color_palette[s->palette_current++] = val;
> @@ -586,6 +641,7 @@ static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp)
> s->vram_bit_mask = MACFB_VRAM_SIZE - 1;
> memory_region_set_coalescing(&s->mem_vram);
>
> + s->vbl_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, macfb_vbl_timer, s);
> macfb_update_mode(s);
> }
>
> @@ -601,6 +657,16 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp)
>
> sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl);
> sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram);
> +
> + qdev_init_gpio_out(dev, &ms->irq, 1);
> +}
> +
> +static void macfb_nubus_set_irq(void *opaque, int n, int level)
> +{
> + MacfbNubusState *s = NUBUS_MACFB(opaque);
> + NubusDevice *nd = NUBUS_DEVICE(s);
> +
> + nubus_set_irq(nd, level);
> }
>
> static void macfb_nubus_realize(DeviceState *dev, Error **errp)
> @@ -622,6 +688,19 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp)
>
> memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl);
> memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram);
> +
> + ms->irq = qemu_allocate_irq(macfb_nubus_set_irq, s, 0);
> +}
> +
> +static void macfb_nubus_unrealize(DeviceState *dev)
> +{
> + MacfbNubusState *s = NUBUS_MACFB(dev);
> + MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev);
> + MacfbState *ms = &s->macfb;
> +
> + ndc->parent_unrealize(dev);
> +
> + qemu_free_irq(ms->irq);
> }
>
> static void macfb_sysbus_reset(DeviceState *d)
> @@ -672,6 +751,8 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data)
>
> device_class_set_parent_realize(dc, macfb_nubus_realize,
> &ndc->parent_realize);
> + device_class_set_parent_unrealize(dc, macfb_nubus_unrealize,
> + &ndc->parent_unrealize);
> dc->desc = "Nubus Macintosh framebuffer";
> dc->reset = macfb_nubus_reset;
> dc->vmsd = &vmstate_macfb;
> diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h
> index 0aff0d84d2..e52775aa21 100644
> --- a/include/hw/display/macfb.h
> +++ b/include/hw/display/macfb.h
> @@ -14,7 +14,9 @@
> #define MACFB_H
>
> #include "exec/memory.h"
> +#include "hw/irq.h"
> #include "ui/console.h"
> +#include "qemu/timer.h"
> #include "qom/object.h"
>
> typedef enum {
> @@ -63,6 +65,11 @@ typedef struct MacfbState {
>
> uint32_t regs[MACFB_NUM_REGS];
> MacFbMode *mode;
> +
> + uint32_t irq_state;
> + uint32_t irq_mask;
> + QEMUTimer *vbl_timer;
> + qemu_irq irq;
> } MacfbState;
>
> #define TYPE_MACFB "sysbus-macfb"
> @@ -81,6 +88,7 @@ struct MacfbNubusDeviceClass {
> DeviceClass parent_class;
>
> DeviceRealize parent_realize;
> + DeviceUnrealize parent_unrealize;
> };
>
>
>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
On 04/10/2021 17:32, Laurent Vivier wrote:
> Le 02/10/2021 à 13:00, Mark Cave-Ayland a écrit :
>> The MacOS driver expects a 60.15Hz vertical blank interrupt to be generated by
>> the framebuffer which in turn schedules the mouse driver via the Vertical Retrace
>> Manager.
>>
>> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
>> ---
>> hw/display/macfb.c | 81 ++++++++++++++++++++++++++++++++++++++
>> include/hw/display/macfb.h | 8 ++++
>> 2 files changed, 89 insertions(+)
>>
>> diff --git a/hw/display/macfb.c b/hw/display/macfb.c
>> index 29f6ad8eba..60a203e67b 100644
>> --- a/hw/display/macfb.c
>> +++ b/hw/display/macfb.c
>> @@ -33,9 +33,16 @@
>> #define DAFB_MODE_CTRL1 0x8
>> #define DAFB_MODE_CTRL2 0xc
>> #define DAFB_MODE_SENSE 0x1c
>> +#define DAFB_INTR_MASK 0x104
>> +#define DAFB_INTR_STAT 0x108
>> +#define DAFB_INTR_CLEAR 0x10c
>> #define DAFB_RESET 0x200
>> #define DAFB_LUT 0x213
>>
>> +#define DAFB_INTR_VBL 0x4
>> +
>> +/* Vertical Blank period (60.15Hz) */
>> +#define DAFB_INTR_VBL_PERIOD_NS 16625800
>>
>> /*
>> * Quadra sense codes taken from Apple Technical Note HW26:
>> @@ -449,6 +456,32 @@ static void macfb_update_display(void *opaque)
>> macfb_draw_graphic(s);
>> }
>>
>> +static void macfb_update_irq(MacfbState *s)
>> +{
>> + uint32_t irq_state = s->irq_state & s->irq_mask;
>> +
>> + if (irq_state) {
>> + qemu_irq_raise(s->irq);
>> + } else {
>> + qemu_irq_lower(s->irq);
>> + }
>> +}
>> +
>> +static void macfb_vbl_timer(void *opaque)
>> +{
>> + MacfbState *s = opaque;
>> + int64_t next_vbl;
>> +
>> + s->irq_state |= DAFB_INTR_VBL;
>> + macfb_update_irq(s);
>> +
>> + /* 60 Hz irq */
>> + next_vbl = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
>> + DAFB_INTR_VBL_PERIOD_NS) /
>> + DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS;
>> + timer_mod(s->vbl_timer, next_vbl);
>
> perhaps you can move this to a function and call it here and below?
I'd like to keep the timer_mod() outside of the function but I agree it is nicer to
keep the next_vbl logci in a single place.
>> +}
>> +
>> static void macfb_reset(MacfbState *s)
>> {
>> int i;
>> @@ -477,6 +510,9 @@ static uint64_t macfb_ctrl_read(void *opaque,
>> case DAFB_MODE_CTRL2:
>> val = s->regs[addr >> 2];
>> break;
>> + case DAFB_INTR_STAT:
>> + val = s->irq_state;
>> + break;
>> case DAFB_MODE_SENSE:
>> val = macfb_sense_read(s);
>> break;
>> @@ -492,6 +528,8 @@ static void macfb_ctrl_write(void *opaque,
>> unsigned int size)
>> {
>> MacfbState *s = opaque;
>> + int64_t next_vbl;
>> +
>> switch (addr) {
>> case DAFB_MODE_VADDR1:
>> case DAFB_MODE_VADDR2:
>> @@ -507,8 +545,25 @@ static void macfb_ctrl_write(void *opaque,
>> case DAFB_MODE_SENSE:
>> macfb_sense_write(s, val);
>> break;
>> + case DAFB_INTR_MASK:
>> + s->irq_mask = val;
>> + if (val & DAFB_INTR_VBL) {
>> + next_vbl = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
>> + DAFB_INTR_VBL_PERIOD_NS) /
>> + DAFB_INTR_VBL_PERIOD_NS * DAFB_INTR_VBL_PERIOD_NS;
>> + timer_mod(s->vbl_timer, next_vbl);
>> + } else {
>> + timer_del(s->vbl_timer);
>> + }
>> + break;
>> + case DAFB_INTR_CLEAR:
>> + s->irq_state &= ~DAFB_INTR_VBL;
>> + macfb_update_irq(s);
>> + break;
>> case DAFB_RESET:
>> s->palette_current = 0;
>> + s->irq_state &= ~DAFB_INTR_VBL;
>> + macfb_update_irq(s);
>> break;
>> case DAFB_LUT:
>> s->color_palette[s->palette_current++] = val;
>> @@ -586,6 +641,7 @@ static void macfb_common_realize(DeviceState *dev, MacfbState *s, Error **errp)
>> s->vram_bit_mask = MACFB_VRAM_SIZE - 1;
>> memory_region_set_coalescing(&s->mem_vram);
>>
>> + s->vbl_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, macfb_vbl_timer, s);
>> macfb_update_mode(s);
>> }
>>
>> @@ -601,6 +657,16 @@ static void macfb_sysbus_realize(DeviceState *dev, Error **errp)
>>
>> sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_ctrl);
>> sysbus_init_mmio(SYS_BUS_DEVICE(s), &ms->mem_vram);
>> +
>> + qdev_init_gpio_out(dev, &ms->irq, 1);
>> +}
>> +
>> +static void macfb_nubus_set_irq(void *opaque, int n, int level)
>> +{
>> + MacfbNubusState *s = NUBUS_MACFB(opaque);
>> + NubusDevice *nd = NUBUS_DEVICE(s);
>> +
>> + nubus_set_irq(nd, level);
>> }
>>
>> static void macfb_nubus_realize(DeviceState *dev, Error **errp)
>> @@ -622,6 +688,19 @@ static void macfb_nubus_realize(DeviceState *dev, Error **errp)
>>
>> memory_region_add_subregion(&nd->slot_mem, DAFB_BASE, &ms->mem_ctrl);
>> memory_region_add_subregion(&nd->slot_mem, VIDEO_BASE, &ms->mem_vram);
>> +
>> + ms->irq = qemu_allocate_irq(macfb_nubus_set_irq, s, 0);
>> +}
>> +
>> +static void macfb_nubus_unrealize(DeviceState *dev)
>> +{
>> + MacfbNubusState *s = NUBUS_MACFB(dev);
>> + MacfbNubusDeviceClass *ndc = NUBUS_MACFB_GET_CLASS(dev);
>> + MacfbState *ms = &s->macfb;
>> +
>> + ndc->parent_unrealize(dev);
>> +
>> + qemu_free_irq(ms->irq);
>> }
>>
>> static void macfb_sysbus_reset(DeviceState *d)
>> @@ -672,6 +751,8 @@ static void macfb_nubus_class_init(ObjectClass *klass, void *data)
>>
>> device_class_set_parent_realize(dc, macfb_nubus_realize,
>> &ndc->parent_realize);
>> + device_class_set_parent_unrealize(dc, macfb_nubus_unrealize,
>> + &ndc->parent_unrealize);
>> dc->desc = "Nubus Macintosh framebuffer";
>> dc->reset = macfb_nubus_reset;
>> dc->vmsd = &vmstate_macfb;
>> diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h
>> index 0aff0d84d2..e52775aa21 100644
>> --- a/include/hw/display/macfb.h
>> +++ b/include/hw/display/macfb.h
>> @@ -14,7 +14,9 @@
>> #define MACFB_H
>>
>> #include "exec/memory.h"
>> +#include "hw/irq.h"
>> #include "ui/console.h"
>> +#include "qemu/timer.h"
>> #include "qom/object.h"
>>
>> typedef enum {
>> @@ -63,6 +65,11 @@ typedef struct MacfbState {
>>
>> uint32_t regs[MACFB_NUM_REGS];
>> MacFbMode *mode;
>> +
>> + uint32_t irq_state;
>> + uint32_t irq_mask;
>> + QEMUTimer *vbl_timer;
>> + qemu_irq irq;
>> } MacfbState;
>>
>> #define TYPE_MACFB "sysbus-macfb"
>> @@ -81,6 +88,7 @@ struct MacfbNubusDeviceClass {
>> DeviceClass parent_class;
>>
>> DeviceRealize parent_realize;
>> + DeviceUnrealize parent_unrealize;
>> };
>>
>>
>>
>
> Reviewed-by: Laurent Vivier <laurent@vivier.eu>
ATB,
Mark.
© 2016 - 2026 Red Hat, Inc.