drm: rework radeon memory map (radeon 1.23)
authorDave Airlie <airlied@linux.ie>
Sun, 19 Mar 2006 08:37:55 +0000 (19:37 +1100)
committerDave Airlie <airlied@linux.ie>
Sun, 19 Mar 2006 08:37:55 +0000 (19:37 +1100)
This code reworks the radeon memory map so it works better
for newer r300 chips and for a lot of older PCI chips.

It really requires a new X driver in order to take advantage of this code.

From: Ben Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Dave Airlie <airlied@linux.ie>
drivers/char/drm/r300_cmdbuf.c
drivers/char/drm/radeon_cp.c
drivers/char/drm/radeon_drm.h
drivers/char/drm/radeon_drv.h
drivers/char/drm/radeon_state.c

index 9fbadb965858d7b112da27843039efa8777f0dfd..20b6cb39213d2a66baebd40a4208cd5cc9f29bfd 100644 (file)
@@ -242,8 +242,10 @@ static __inline__ int r300_check_range(unsigned reg, int count)
        return 0;
 }
 
-  /* we expect offsets passed to the framebuffer to be either within video memory or
-     within AGP space */
+/*
+ * we expect offsets passed to the framebuffer to be either within video 
+ * memory or within AGP space 
+ */
 static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv,
                                        u32 offset)
 {
@@ -251,11 +253,11 @@ static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv,
           but this value is not being kept.
           This code is correct for now (does the same thing as the
           code that sets MC_FB_LOCATION) in radeon_cp.c */
-       if ((offset >= dev_priv->fb_location) &&
-           (offset < dev_priv->gart_vm_start))
+       if (offset >= dev_priv->fb_location &&
+           offset < (dev_priv->fb_location + dev_priv->fb_size))
                return 0;
-       if ((offset >= dev_priv->gart_vm_start) &&
-           (offset < dev_priv->gart_vm_start + dev_priv->gart_size))
+       if (offset >= dev_priv->gart_vm_start &&
+           offset < (dev_priv->gart_vm_start + dev_priv->gart_size))
                return 0;
        return 1;
 }
@@ -490,6 +492,7 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv,
 
        return 0;
 }
+
 static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv,
                                             drm_radeon_kcmd_buffer_t *cmdbuf)
 {
index 9bb8ae0c1c2720a31794179364fca10d83c1fb3e..cc4942a112cb29781ea633dd532ac1d0a2a16c23 100644 (file)
@@ -1118,14 +1118,20 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
 {
        u32 ring_start, cur_read_ptr;
        u32 tmp;
-
-       /* Initialize the memory controller */
-       RADEON_WRITE(RADEON_MC_FB_LOCATION,
-                    ((dev_priv->gart_vm_start - 1) & 0xffff0000)
-                    | (dev_priv->fb_location >> 16));
+       
+       /* Initialize the memory controller. With new memory map, the fb location
+        * is not changed, it should have been properly initialized already. Part
+        * of the problem is that the code below is bogus, assuming the GART is
+        * always appended to the fb which is not necessarily the case
+        */
+       if (!dev_priv->new_memmap)
+               RADEON_WRITE(RADEON_MC_FB_LOCATION,
+                            ((dev_priv->gart_vm_start - 1) & 0xffff0000)
+                            | (dev_priv->fb_location >> 16));
 
 #if __OS_HAS_AGP
        if (dev_priv->flags & CHIP_IS_AGP) {
+               RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base);
                RADEON_WRITE(RADEON_MC_AGP_LOCATION,
                             (((dev_priv->gart_vm_start - 1 +
                                dev_priv->gart_size) & 0xffff0000) |
@@ -1153,8 +1159,6 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
 
 #if __OS_HAS_AGP
        if (dev_priv->flags & CHIP_IS_AGP) {
-               /* set RADEON_AGP_BASE here instead of relying on X from user space */
-               RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base);
                RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR,
                             dev_priv->ring_rptr->offset
                             - dev->agp->base + dev_priv->gart_vm_start);
@@ -1174,6 +1178,17 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
                          entry->handle + tmp_ofs);
        }
 
+       /* Set ring buffer size */
+#ifdef __BIG_ENDIAN
+       RADEON_WRITE(RADEON_CP_RB_CNTL,
+                    dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT);
+#else
+       RADEON_WRITE(RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw);
+#endif
+
+       /* Start with assuming that writeback doesn't work */
+       dev_priv->writeback_works = 0;
+
        /* Initialize the scratch register pointer.  This will cause
         * the scratch register values to be written out to memory
         * whenever they are updated.
@@ -1190,28 +1205,9 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
 
        RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7);
 
-       /* Writeback doesn't seem to work everywhere, test it first */
-       DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0);
-       RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef);
-
-       for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) {
-               if (DRM_READ32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1)) ==
-                   0xdeadbeef)
-                       break;
-               DRM_UDELAY(1);
-       }
-
-       if (tmp < dev_priv->usec_timeout) {
-               dev_priv->writeback_works = 1;
-               DRM_DEBUG("writeback test succeeded, tmp=%d\n", tmp);
-       } else {
-               dev_priv->writeback_works = 0;
-               DRM_DEBUG("writeback test failed\n");
-       }
-       if (radeon_no_wb == 1) {
-               dev_priv->writeback_works = 0;
-               DRM_DEBUG("writeback forced off\n");
-       }
+       /* Turn on bus mastering */
+       tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
+       RADEON_WRITE(RADEON_BUS_CNTL, tmp);
 
        dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0;
        RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame);
@@ -1223,26 +1219,45 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
        dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0;
        RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear);
 
-       /* Set ring buffer size */
-#ifdef __BIG_ENDIAN
-       RADEON_WRITE(RADEON_CP_RB_CNTL,
-                    dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT);
-#else
-       RADEON_WRITE(RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw);
-#endif
-
        radeon_do_wait_for_idle(dev_priv);
 
-       /* Turn on bus mastering */
-       tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
-       RADEON_WRITE(RADEON_BUS_CNTL, tmp);
-
        /* Sync everything up */
        RADEON_WRITE(RADEON_ISYNC_CNTL,
                     (RADEON_ISYNC_ANY2D_IDLE3D |
                      RADEON_ISYNC_ANY3D_IDLE2D |
                      RADEON_ISYNC_WAIT_IDLEGUI |
                      RADEON_ISYNC_CPSCRATCH_IDLEGUI));
+
+}
+
+static void radeon_test_writeback(drm_radeon_private_t * dev_priv)
+{
+       u32 tmp;
+
+       /* Writeback doesn't seem to work everywhere, test it here and possibly
+        * enable it if it appears to work
+        */
+       DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0);
+       RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef);
+
+       for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) {
+               if (DRM_READ32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1)) ==
+                   0xdeadbeef)
+                       break;
+               DRM_UDELAY(1);
+       }
+
+       if (tmp < dev_priv->usec_timeout) {
+               dev_priv->writeback_works = 1;
+               DRM_INFO("writeback test succeeded in %d usecs\n", tmp);
+       } else {
+               dev_priv->writeback_works = 0;
+               DRM_INFO("writeback test failed\n");
+       }
+       if (radeon_no_wb == 1) {
+               dev_priv->writeback_works = 0;
+               DRM_INFO("writeback forced off\n");
+       }
 }
 
 /* Enable or disable PCI-E GART on the chip */
@@ -1496,6 +1511,9 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
 
        dev_priv->fb_location = (RADEON_READ(RADEON_MC_FB_LOCATION)
                                 & 0xffff) << 16;
+       dev_priv->fb_size = 
+               ((RADEON_READ(RADEON_MC_FB_LOCATION) & 0xffff0000u) + 0x10000)
+               - dev_priv->fb_location;
 
        dev_priv->front_pitch_offset = (((dev_priv->front_pitch / 64) << 22) |
                                        ((dev_priv->front_offset
@@ -1510,8 +1528,46 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
                                          + dev_priv->fb_location) >> 10));
 
        dev_priv->gart_size = init->gart_size;
-       dev_priv->gart_vm_start = dev_priv->fb_location
-           + RADEON_READ(RADEON_CONFIG_APER_SIZE);
+
+       /* New let's set the memory map ... */
+       if (dev_priv->new_memmap) {
+               u32 base = 0;
+
+               DRM_INFO("Setting GART location based on new memory map\n");
+
+               /* If using AGP, try to locate the AGP aperture at the same
+                * location in the card and on the bus, though we have to
+                * align it down.
+                */
+#if __OS_HAS_AGP
+               if (dev_priv->flags & CHIP_IS_AGP) {
+                       base = dev->agp->base;
+                       /* Check if valid */
+                       if ((base + dev_priv->gart_size) > dev_priv->fb_location &&
+                           base < (dev_priv->fb_location + dev_priv->fb_size)) {
+                               DRM_INFO("Can't use AGP base @0x%08lx, won't fit\n",
+                                        dev->agp->base);
+                               base = 0;
+                       }
+               }
+#endif
+               /* If not or if AGP is at 0 (Macs), try to put it elsewhere */
+               if (base == 0) {
+                       base = dev_priv->fb_location + dev_priv->fb_size;
+                       if (((base + dev_priv->gart_size) & 0xfffffffful)
+                           < base)
+                               base = dev_priv->fb_location
+                                       - dev_priv->gart_size;
+               }               
+               dev_priv->gart_vm_start = base & 0xffc00000u;
+               if (dev_priv->gart_vm_start != base)
+                       DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n",
+                                base, dev_priv->gart_vm_start);
+       } else {
+               DRM_INFO("Setting GART location based on old memory map\n");
+               dev_priv->gart_vm_start = dev_priv->fb_location +
+                       RADEON_READ(RADEON_CONFIG_APER_SIZE);
+       }
 
 #if __OS_HAS_AGP
        if (dev_priv->flags & CHIP_IS_AGP)
@@ -1596,6 +1652,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
        dev_priv->last_buf = 0;
 
        radeon_do_engine_reset(dev);
+       radeon_test_writeback(dev_priv);
 
        return 0;
 }
index 9c177a6b2a4c46ed953a28a79d34eadd1cfbffd3..bb63e6806fd13637927e08438a772398d2dff308 100644 (file)
@@ -697,6 +697,7 @@ typedef struct drm_radeon_setparam {
 #define RADEON_SETPARAM_FB_LOCATION    1       /* determined framebuffer location */
 #define RADEON_SETPARAM_SWITCH_TILING  2       /* enable/disable color tiling */
 #define RADEON_SETPARAM_PCIGART_LOCATION 3     /* PCI Gart Location */
+#define RADEON_SETPARAM_NEW_MEMMAP 4           /* Use new memory map */
 
 /* 1.14: Clients can allocate/free a surface
  */
index 1f7d2ab8c4fcb4de415f046f09fd65fe966f9164..a0c198895a27da13896401f873bcebc0f31334c8 100644 (file)
@@ -38,7 +38,7 @@
 
 #define DRIVER_NAME            "radeon"
 #define DRIVER_DESC            "ATI Radeon"
-#define DRIVER_DATE            "20051229"
+#define DRIVER_DATE            "20060225"
 
 /* Interface history:
  *
  * 1.20- Add support for r300 texrect
  * 1.21- Add support for card type getparam
  * 1.22- Add support for texture cache flushes (R300_TX_CNTL)
+ * 1.23- Add new radeon memory map work from benh
  */
 #define DRIVER_MAJOR           1
-#define DRIVER_MINOR           22
+#define DRIVER_MINOR           23
 #define DRIVER_PATCHLEVEL      0
 
 /*
@@ -138,7 +139,8 @@ enum radeon_chip_flags {
        CHIP_IS_PCIE = 0x00200000UL,
 };
 
-#define GET_RING_HEAD(dev_priv)                DRM_READ32(  (dev_priv)->ring_rptr, 0 )
+#define GET_RING_HEAD(dev_priv)        (dev_priv->writeback_works ? \
+        DRM_READ32(  (dev_priv)->ring_rptr, 0 ) : RADEON_READ(RADEON_CP_RB_RPTR))
 #define SET_RING_HEAD(dev_priv,val)    DRM_WRITE32( (dev_priv)->ring_rptr, 0, (val) )
 
 typedef struct drm_radeon_freelist {
@@ -199,6 +201,8 @@ typedef struct drm_radeon_private {
        drm_radeon_sarea_t *sarea_priv;
 
        u32 fb_location;
+       u32 fb_size;
+       int new_memmap;
 
        int gart_size;
        u32 gart_vm_start;
index 7bc27516d42550d7cde72a37a24227391f59ae26..56e56b873616076ba3d93699bbc6eab66c0c1c57 100644 (file)
@@ -45,22 +45,53 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
        u32 off = *offset;
        struct drm_radeon_driver_file_fields *radeon_priv;
 
-       if (off >= dev_priv->fb_location &&
-           off < (dev_priv->gart_vm_start + dev_priv->gart_size))
-               return 0;
-
-       radeon_priv = filp_priv->driver_priv;
-       off += radeon_priv->radeon_fb_delta;
+       /* Hrm ... the story of the offset ... So this function converts
+        * the various ideas of what userland clients might have for an
+        * offset in the card address space into an offset into the card
+        * address space :) So with a sane client, it should just keep
+        * the value intact and just do some boundary checking. However,
+        * not all clients are sane. Some older clients pass us 0 based
+        * offsets relative to the start of the framebuffer and some may
+        * assume the AGP aperture it appended to the framebuffer, so we
+        * try to detect those cases and fix them up.
+        *
+        * Note: It might be a good idea here to make sure the offset lands
+        * in some "allowed" area to protect things like the PCIE GART...
+        */
 
-       DRM_DEBUG("offset fixed up to 0x%x\n", off);
+       /* First, the best case, the offset already lands in either the
+        * framebuffer or the GART mapped space
+        */
+       if ((off >= dev_priv->fb_location &&
+            off < (dev_priv->fb_location + dev_priv->fb_size)) ||
+           (off >= dev_priv->gart_vm_start &&
+            off < (dev_priv->gart_vm_start + dev_priv->gart_size)))
+               return 0;
 
-       if (off < dev_priv->fb_location ||
-           off >= (dev_priv->gart_vm_start + dev_priv->gart_size))
-               return DRM_ERR(EINVAL);
+       /* Ok, that didn't happen... now check if we have a zero based
+        * offset that fits in the framebuffer + gart space, apply the
+        * magic offset we get from SETPARAM or calculated from fb_location
+        */
+       if (off < (dev_priv->fb_size + dev_priv->gart_size)) {
+               radeon_priv = filp_priv->driver_priv;
+               off += radeon_priv->radeon_fb_delta;
+       }
 
-       *offset = off;
+       /* Finally, assume we aimed at a GART offset if beyond the fb */
+       if (off > (dev_priv->fb_location + dev_priv->fb_size))
+               off = off - (dev_priv->fb_location + dev_priv->fb_size) +
+                       dev_priv->gart_vm_start;
 
-       return 0;
+       /* Now recheck and fail if out of bounds */
+       if ((off >= dev_priv->fb_location &&
+            off < (dev_priv->fb_location + dev_priv->fb_size)) ||
+           (off >= dev_priv->gart_vm_start &&
+            off < (dev_priv->gart_vm_start + dev_priv->gart_size))) {
+               DRM_DEBUG("offset fixed up to 0x%x\n", off);
+               *offset = off;
+               return 0;
+       }
+       return DRM_ERR(EINVAL);
 }
 
 static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
@@ -3012,6 +3043,9 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS)
        case RADEON_SETPARAM_PCIGART_LOCATION:
                dev_priv->pcigart_offset = sp.value;
                break;
+       case RADEON_SETPARAM_NEW_MEMMAP:
+               dev_priv->new_memmap = sp.value;
+               break;
        default:
                DRM_DEBUG("Invalid parameter %d\n", sp.param);
                return DRM_ERR(EINVAL);