qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH 11/12] macfb: add vertical blank interrupt


From: Mark Cave-Ayland
Subject: Re: [PATCH 11/12] macfb: add vertical blank interrupt
Date: Mon, 4 Oct 2021 19:52:44 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.14.0

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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]