Commit d5ea702f authored by Dave Airlie's avatar Dave Airlie

drm: rework radeon memory map (radeon 1.23)

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: default avatarDave Airlie <airlied@linux.ie>
parent 45f17100
...@@ -242,8 +242,10 @@ static __inline__ int r300_check_range(unsigned reg, int count) ...@@ -242,8 +242,10 @@ static __inline__ int r300_check_range(unsigned reg, int count)
return 0; 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, static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv,
u32 offset) u32 offset)
{ {
...@@ -251,11 +253,11 @@ static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv, ...@@ -251,11 +253,11 @@ static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv,
but this value is not being kept. but this value is not being kept.
This code is correct for now (does the same thing as the This code is correct for now (does the same thing as the
code that sets MC_FB_LOCATION) in radeon_cp.c */ code that sets MC_FB_LOCATION) in radeon_cp.c */
if ((offset >= dev_priv->fb_location) && if (offset >= dev_priv->fb_location &&
(offset < dev_priv->gart_vm_start)) offset < (dev_priv->fb_location + dev_priv->fb_size))
return 0; return 0;
if ((offset >= dev_priv->gart_vm_start) && if (offset >= dev_priv->gart_vm_start &&
(offset < dev_priv->gart_vm_start + dev_priv->gart_size)) offset < (dev_priv->gart_vm_start + dev_priv->gart_size))
return 0; return 0;
return 1; return 1;
} }
...@@ -490,6 +492,7 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv, ...@@ -490,6 +492,7 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv,
return 0; return 0;
} }
static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv, static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv,
drm_radeon_kcmd_buffer_t *cmdbuf) drm_radeon_kcmd_buffer_t *cmdbuf)
{ {
......
...@@ -1118,14 +1118,20 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, ...@@ -1118,14 +1118,20 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
{ {
u32 ring_start, cur_read_ptr; u32 ring_start, cur_read_ptr;
u32 tmp; u32 tmp;
/* Initialize the memory controller */ /* Initialize the memory controller. With new memory map, the fb location
RADEON_WRITE(RADEON_MC_FB_LOCATION, * is not changed, it should have been properly initialized already. Part
((dev_priv->gart_vm_start - 1) & 0xffff0000) * of the problem is that the code below is bogus, assuming the GART is
| (dev_priv->fb_location >> 16)); * 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 __OS_HAS_AGP
if (dev_priv->flags & CHIP_IS_AGP) { if (dev_priv->flags & CHIP_IS_AGP) {
RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base);
RADEON_WRITE(RADEON_MC_AGP_LOCATION, RADEON_WRITE(RADEON_MC_AGP_LOCATION,
(((dev_priv->gart_vm_start - 1 + (((dev_priv->gart_vm_start - 1 +
dev_priv->gart_size) & 0xffff0000) | dev_priv->gart_size) & 0xffff0000) |
...@@ -1153,8 +1159,6 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, ...@@ -1153,8 +1159,6 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
#if __OS_HAS_AGP #if __OS_HAS_AGP
if (dev_priv->flags & CHIP_IS_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, RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR,
dev_priv->ring_rptr->offset dev_priv->ring_rptr->offset
- dev->agp->base + dev_priv->gart_vm_start); - dev->agp->base + dev_priv->gart_vm_start);
...@@ -1174,6 +1178,17 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, ...@@ -1174,6 +1178,17 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
entry->handle + tmp_ofs); 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 /* Initialize the scratch register pointer. This will cause
* the scratch register values to be written out to memory * the scratch register values to be written out to memory
* whenever they are updated. * whenever they are updated.
...@@ -1190,28 +1205,9 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, ...@@ -1190,28 +1205,9 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7); RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7);
/* Writeback doesn't seem to work everywhere, test it first */ /* Turn on bus mastering */
DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0); tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef); RADEON_WRITE(RADEON_BUS_CNTL, tmp);
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");
}
dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0; dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0;
RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame); 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, ...@@ -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; dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0;
RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear); 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); 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 */ /* Sync everything up */
RADEON_WRITE(RADEON_ISYNC_CNTL, RADEON_WRITE(RADEON_ISYNC_CNTL,
(RADEON_ISYNC_ANY2D_IDLE3D | (RADEON_ISYNC_ANY2D_IDLE3D |
RADEON_ISYNC_ANY3D_IDLE2D | RADEON_ISYNC_ANY3D_IDLE2D |
RADEON_ISYNC_WAIT_IDLEGUI | RADEON_ISYNC_WAIT_IDLEGUI |
RADEON_ISYNC_CPSCRATCH_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 */ /* 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) ...@@ -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) dev_priv->fb_location = (RADEON_READ(RADEON_MC_FB_LOCATION)
& 0xffff) << 16; & 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_pitch_offset = (((dev_priv->front_pitch / 64) << 22) |
((dev_priv->front_offset ((dev_priv->front_offset
...@@ -1510,8 +1528,46 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) ...@@ -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->fb_location) >> 10));
dev_priv->gart_size = init->gart_size; 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 __OS_HAS_AGP
if (dev_priv->flags & CHIP_IS_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) ...@@ -1596,6 +1652,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
dev_priv->last_buf = 0; dev_priv->last_buf = 0;
radeon_do_engine_reset(dev); radeon_do_engine_reset(dev);
radeon_test_writeback(dev_priv);
return 0; return 0;
} }
......
...@@ -697,6 +697,7 @@ typedef struct drm_radeon_setparam { ...@@ -697,6 +697,7 @@ typedef struct drm_radeon_setparam {
#define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ #define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */
#define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ #define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */
#define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ #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 /* 1.14: Clients can allocate/free a surface
*/ */
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
#define DRIVER_NAME "radeon" #define DRIVER_NAME "radeon"
#define DRIVER_DESC "ATI Radeon" #define DRIVER_DESC "ATI Radeon"
#define DRIVER_DATE "20051229" #define DRIVER_DATE "20060225"
/* Interface history: /* Interface history:
* *
...@@ -91,9 +91,10 @@ ...@@ -91,9 +91,10 @@
* 1.20- Add support for r300 texrect * 1.20- Add support for r300 texrect
* 1.21- Add support for card type getparam * 1.21- Add support for card type getparam
* 1.22- Add support for texture cache flushes (R300_TX_CNTL) * 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_MAJOR 1
#define DRIVER_MINOR 22 #define DRIVER_MINOR 23
#define DRIVER_PATCHLEVEL 0 #define DRIVER_PATCHLEVEL 0
/* /*
...@@ -138,7 +139,8 @@ enum radeon_chip_flags { ...@@ -138,7 +139,8 @@ enum radeon_chip_flags {
CHIP_IS_PCIE = 0x00200000UL, 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) ) #define SET_RING_HEAD(dev_priv,val) DRM_WRITE32( (dev_priv)->ring_rptr, 0, (val) )
typedef struct drm_radeon_freelist { typedef struct drm_radeon_freelist {
...@@ -199,6 +201,8 @@ typedef struct drm_radeon_private { ...@@ -199,6 +201,8 @@ typedef struct drm_radeon_private {
drm_radeon_sarea_t *sarea_priv; drm_radeon_sarea_t *sarea_priv;
u32 fb_location; u32 fb_location;
u32 fb_size;
int new_memmap;
int gart_size; int gart_size;
u32 gart_vm_start; u32 gart_vm_start;
......
...@@ -45,22 +45,53 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * ...@@ -45,22 +45,53 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
u32 off = *offset; u32 off = *offset;
struct drm_radeon_driver_file_fields *radeon_priv; struct drm_radeon_driver_file_fields *radeon_priv;
if (off >= dev_priv->fb_location && /* Hrm ... the story of the offset ... So this function converts
off < (dev_priv->gart_vm_start + dev_priv->gart_size)) * the various ideas of what userland clients might have for an
return 0; * offset in the card address space into an offset into the card
* address space :) So with a sane client, it should just keep
radeon_priv = filp_priv->driver_priv; * the value intact and just do some boundary checking. However,
off += radeon_priv->radeon_fb_delta; * 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 || /* Ok, that didn't happen... now check if we have a zero based
off >= (dev_priv->gart_vm_start + dev_priv->gart_size)) * offset that fits in the framebuffer + gart space, apply the
return DRM_ERR(EINVAL); * 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 * static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
...@@ -3012,6 +3043,9 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS) ...@@ -3012,6 +3043,9 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS)
case RADEON_SETPARAM_PCIGART_LOCATION: case RADEON_SETPARAM_PCIGART_LOCATION:
dev_priv->pcigart_offset = sp.value; dev_priv->pcigart_offset = sp.value;
break; break;
case RADEON_SETPARAM_NEW_MEMMAP:
dev_priv->new_memmap = sp.value;
break;
default: default:
DRM_DEBUG("Invalid parameter %d\n", sp.param); DRM_DEBUG("Invalid parameter %d\n", sp.param);
return DRM_ERR(EINVAL); return DRM_ERR(EINVAL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment