The r100's internal address space has mappings to the framebuffer,
PCI GART and system memory.

The framebuffer mapping is controlled by the MC_FB_LOCATION register
which contains a start and a top address.

The PCI GART is set up and mapped by the AIC_* registers. When enabled with
AIC_CTRL, addresses between AIC_LO_ADDR and AIC_HI_ADDR are translated using
the page table at AIC_PT_BASE. The physical address can then be accessed
via DMA.

If the address is outside either of these regions it is treated as a physical
address.

This is required for using the CP engine. Drivers store the ring buffer
and indirect buffer in the PCI GART area. The r100 2D engine also stores GPU
addresses in the src/dst offset registers that require routing. A future
patch integrates this into the ati_2d blit operations.

Signed-off-by: Chad Jablonski <[email protected]>
---
 hw/display/ati.c      | 83 +++++++++++++++++++++++++++++++++++++++++++
 hw/display/ati_int.h  | 11 ++++++
 hw/display/ati_regs.h |  7 ++++
 3 files changed, 101 insertions(+)

diff --git a/hw/display/ati.c b/hw/display/ati.c
index 5c1232a31f..f19a0613d7 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -277,6 +277,55 @@ static inline uint32_t ati_reg_read_offs(uint32_t reg, int 
offs,
     }
 }
 
+ATIMemRoute ati_mc_route(ATIVGAState *s, uint32_t gpu_addr)
+{
+    uint32_t fb_start = (s->regs.mc_fb_location & 0xffff) << 16;
+    uint32_t fb_top = s->regs.mc_fb_location | 0xffff;
+
+    /* Framebuffer */
+    if (gpu_addr >= fb_start && gpu_addr <= fb_top) {
+        uint32_t offs = (gpu_addr - fb_start) % s->vga.vram_size;
+        return (ATIMemRoute) { .is_vram = true, .addr = offs };
+    }
+
+    /* PCI GART */
+    if ((s->regs.aic_ctrl & PCIGART_TRANSLATE_EN) &&
+       /* aic_{lo,hi}_addr are page-aligned and inclusive */
+       gpu_addr >= s->regs.aic_lo_addr &&
+       gpu_addr < s->regs.aic_hi_addr + 0x1000) {
+        uint32_t pg_idx = (gpu_addr - s->regs.aic_lo_addr) >> 12;
+        uint32_t pte;
+        if (ldl_le_pci_dma(&s->dev,
+                           s->regs.aic_pt_base + pg_idx * sizeof(uint32_t),
+                           &pte, MEMTXATTRS_UNSPECIFIED)) {
+            qemu_log_mask(LOG_GUEST_ERROR, "PCI GART pte read failed\n");
+            return (ATIMemRoute) { .is_vram = false, .addr = 0 };
+        }
+        return (ATIMemRoute) {
+            .is_vram = false,
+            .addr = (pte & 0xfffff000) | (gpu_addr & 0xfff),
+        };
+    }
+
+    /* System memory */
+    return (ATIMemRoute) { .is_vram = false, .addr = gpu_addr };
+}
+
+static uint32_t ati_mc_read(ATIVGAState *s, uint32_t gpu_addr)
+{
+    ATIMemRoute route = ati_mc_route(s, gpu_addr);
+    uint32_t val;
+
+    if (route.is_vram) {
+        return ldl_le_p(s->vga.vram_ptr + route.addr);
+    }
+    if (ldl_le_pci_dma(&s->dev, route.addr, &val, MEMTXATTRS_UNSPECIFIED)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "MC DMA read failed\n");
+        return 0;
+    }
+    return val;
+}
+
 static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size)
 {
     ATIVGAState *s = opaque;
@@ -542,6 +591,21 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, 
unsigned int size)
         qemu_log_mask(LOG_GUEST_ERROR,
                       "Read from write-only register 0x%x\n", (unsigned)addr);
         break;
+    case AIC_CTRL:
+        val = s->regs.aic_ctrl;
+        break;
+    case AIC_PT_BASE:
+        val = s->regs.aic_pt_base;
+        break;
+    case AIC_LO_ADDR:
+        val = s->regs.aic_lo_addr;
+        break;
+    case AIC_HI_ADDR:
+        val = s->regs.aic_hi_addr;
+        break;
+    case MC_FB_LOCATION:
+        val = s->regs.mc_fb_location;
+        break;
     default:
         break;
     }
@@ -1046,6 +1110,25 @@ static void ati_mm_write(void *opaque, hwaddr addr,
             s->host_data.next = 0;
         }
         break;
+    case AIC_CTRL:
+        s->regs.aic_ctrl = data;
+        break;
+    case AIC_PT_BASE:
+        s->regs.aic_pt_base = data & 0xfffff000;
+        break;
+    case AIC_LO_ADDR:
+        s->regs.aic_lo_addr = data & 0xfffff000;
+        break;
+    case AIC_HI_ADDR:
+        s->regs.aic_hi_addr = data & 0xfffff000;
+        break;
+    case MC_FB_LOCATION:
+        /*
+         * The bottom 6 bits of fb start are hard-wired to 0
+         * and the bottom 6 bits of fb top are hard-wired to 1
+         */
+        s->regs.mc_fb_location = (data & 0xffc0ffc0) | 0x003f0000;
+        break;
     default:
         break;
     }
diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h
index 0c48934d33..bb08e0c23d 100644
--- a/hw/display/ati_int.h
+++ b/hw/display/ati_int.h
@@ -95,6 +95,11 @@ typedef struct ATIVGARegs {
     uint16_t default_sc_bottom;
     uint16_t default_sc_right;
     uint32_t default_tile;
+    uint32_t aic_ctrl;
+    uint32_t aic_pt_base;
+    uint32_t aic_lo_addr;
+    uint32_t aic_hi_addr;
+    uint32_t mc_fb_location;
 } ATIVGARegs;
 
 typedef struct ATIHostDataState {
@@ -127,8 +132,14 @@ struct ATIVGAState {
     ATIHostDataState host_data;
 };
 
+typedef struct {
+    bool is_vram;
+    hwaddr addr;
+} ATIMemRoute;
+
 const char *ati_reg_name(int num);
 
+ATIMemRoute ati_mc_route(ATIVGAState *s, uint32_t gpu_addr);
 void ati_2d_blt(ATIVGAState *s);
 bool ati_host_data_flush(ATIVGAState *s);
 void ati_host_data_finish(ATIVGAState *s);
diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
index b813fa119e..5e9396bd7a 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -73,6 +73,10 @@
 #define MPP_TB_CONFIG                           0x01C0
 #define MPP_GP_CONFIG                           0x01C8
 #define VIPH_CONTROL                            0x01D0
+#define AIC_CTRL                                0x01D0
+#define AIC_PT_BASE                             0x01D8
+#define AIC_LO_ADDR                             0x01DC
+#define AIC_HI_ADDR                             0x01E0
 #define CRTC_H_TOTAL_DISP                       0x0200
 #define CRTC_H_SYNC_STRT_WID                    0x0204
 #define CRTC_V_TOTAL_DISP                       0x0208
@@ -477,6 +481,9 @@
 /* CRTC2_GEN_CNTL constants */
 #define CRTC2_EN                                0x02000000
 
+/* AIC_CTRL constants */
+#define PCIGART_TRANSLATE_EN                    0x00000001
+
 /* POWER_MANAGEMENT constants */
 #define PWR_MGT_ON                              0x00000001
 #define PWR_MGT_MODE_MASK                       0x00000006
-- 
2.54.0


Reply via email to