diff options
| author | rafal <rafal@NetBSD.org> | 2009-04-21 01:15:37 +0000 |
|---|---|---|
| committer | rafal <rafal@NetBSD.org> | 2009-04-21 01:15:37 +0000 |
| commit | a566e67fbe031face017afaf7cc419cfa73f4ad9 (patch) | |
| tree | 8309c17c1d786d66dae7eaf1ac34a9ec73db0912 /sys/dev/drm | |
| parent | 091b54f6023e902b589e90716b7fb549472c71cb (diff) | |
Rework scatter / gather page allocation inspired by r186295 of FreeBSD SVN:
rework drm_scatter.c which allocates scatter / gather pages for use by
ati pci gart to use bus_dma to handle the allocations. This fixes
a garbled screen issue on at least some radeons (X1400 tested). It is
also likely that this is the correct fix for PR# 119324, though that
is not confirmed yet.
Fixes similar issues on a Radeon Mobility X300, non-Mobility X800 (including
a hard-lock of the machine).
With feedback from ad@, ok mrg@
Diffstat (limited to 'sys/dev/drm')
| -rw-r--r-- | sys/dev/drm/drmP.h | 19 | ||||
| -rw-r--r-- | sys/dev/drm/drm_drv.c | 8 | ||||
| -rw-r--r-- | sys/dev/drm/drm_scatter.c | 164 |
3 files changed, 135 insertions, 56 deletions
diff --git a/sys/dev/drm/drmP.h b/sys/dev/drm/drmP.h index 91bf07d3b53..3d71b1eb630 100644 --- a/sys/dev/drm/drmP.h +++ b/sys/dev/drm/drmP.h @@ -1,4 +1,4 @@ -/* $NetBSD: drmP.h,v 1.33 2009/03/29 17:00:50 mrg Exp $ */ +/* $NetBSD: drmP.h,v 1.34 2009/04/21 01:15:37 rafal Exp $ */ /* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com @@ -706,11 +706,13 @@ typedef struct drm_agp_head { } drm_agp_head_t; typedef struct drm_sg_mem { - unsigned long handle; - void *virtual; - int pages; - dma_addr_t *busaddr; - drm_dma_handle_t *dmah; /* Handle to PCI memory for ATI PCIGART table */ + unsigned long handle; + void *virtual; + int pages; + dma_addr_t *busaddr; + struct drm_dma_handle *sg_dmah; /* Handle for sg_pages */ + struct drm_dma_handle *dmah; /* Handle to PCI memory */ + /* for ATI PCIGART table */ } drm_sg_mem_t; typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t; @@ -1065,6 +1067,7 @@ int drm_agp_unbind(drm_device_t *dev, drm_agp_binding_t *request); /* Scatter Gather Support (drm_scatter.c) */ void drm_sg_cleanup(drm_sg_mem_t *entry); +int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request); #if defined(__FreeBSD__) || defined (__NetBSD__) /* sysctl support (drm_sysctl.h) */ @@ -1139,8 +1142,8 @@ int drm_agp_unbind_ioctl(DRM_IOCTL_ARGS); int drm_agp_bind_ioctl(DRM_IOCTL_ARGS); /* Scatter Gather Support (drm_scatter.c) */ -int drm_sg_alloc(DRM_IOCTL_ARGS); -int drm_sg_free(DRM_IOCTL_ARGS); +int drm_sg_alloc_ioctl(DRM_IOCTL_ARGS); +int drm_sg_free_ioctl(DRM_IOCTL_ARGS); /* consistent PCI memory functions (drm_pci.c) */ drm_dma_handle_t *drm_pci_alloc(drm_device_t *dev, size_t size, size_t align, diff --git a/sys/dev/drm/drm_drv.c b/sys/dev/drm/drm_drv.c index 3ea8c129da9..e4fa0dd9557 100644 --- a/sys/dev/drm/drm_drv.c +++ b/sys/dev/drm/drm_drv.c @@ -1,4 +1,4 @@ -/* $NetBSD: drm_drv.c,v 1.23 2009/04/17 19:02:33 joerg Exp $ */ +/* $NetBSD: drm_drv.c,v 1.24 2009/04/21 01:15:37 rafal Exp $ */ /* drm_drv.h -- Generic driver template -*- linux-c -*- * Created: Thu Nov 23 03:10:50 2000 by gareth@valinux.com @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: drm_drv.c,v 1.23 2009/04/17 19:02:33 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: drm_drv.c,v 1.24 2009/04/21 01:15:37 rafal Exp $"); /* __FBSDID("$FreeBSD: src/sys/dev/drm/drm_drv.c,v 1.6 2006/09/07 23:04:47 anholt Exp $"); */ @@ -114,8 +114,8 @@ static drm_ioctl_desc_t drm_ioctls[256] = { [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, - [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)] = { drm_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, - [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, + [DRM_IOCTL_NR(DRM_IOCTL_SG_ALLOC)] = { drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, + [DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)] = { drm_sg_free_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, [DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = { drm_wait_vblank, 0 }, }; diff --git a/sys/dev/drm/drm_scatter.c b/sys/dev/drm/drm_scatter.c index 90326df1385..baa4cc6d630 100644 --- a/sys/dev/drm/drm_scatter.c +++ b/sys/dev/drm/drm_scatter.c @@ -1,4 +1,4 @@ -/* $NetBSD: drm_scatter.c,v 1.7 2008/07/07 00:33:23 mrg Exp $ */ +/* $NetBSD: drm_scatter.c,v 1.8 2009/04/21 01:15:37 rafal Exp $ */ /* drm_scatter.h -- IOCTLs to manage scatter/gather memory -*- linux-c -*- * Created: Mon Dec 18 23:20:54 2000 by gareth@valinux.com */ @@ -32,73 +32,107 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: drm_scatter.c,v 1.7 2008/07/07 00:33:23 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: drm_scatter.c,v 1.8 2009/04/21 01:15:37 rafal Exp $"); /* __FBSDID("$FreeBSD: src/sys/dev/drm/drm_scatter.c,v 1.3 2006/05/17 06:29:36 anholt Exp $"); */ -#include "drmP.h" - -#define DEBUG_SCATTER 0 +/** @file drm_scatter.c + * Allocation of memory for scatter-gather mappings by the graphics chip. + * + * The memory allocated here is then made into an aperture in the card + * by drm_ati_pcigart_init(). + */ -void drm_sg_cleanup(drm_sg_mem_t *entry) -{ - free((void *)entry->handle, M_DRM); - free(entry->busaddr, M_DRM); - free(entry, M_DRM); -} +#include "drmP.h" -int drm_sg_alloc(DRM_IOCTL_ARGS) +int +drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request) { - DRM_DEVICE; - drm_scatter_gather_t request; drm_sg_mem_t *entry; + struct drm_dma_handle *dmah; unsigned long pages; - int i; - - DRM_DEBUG( "%s\n", __FUNCTION__ ); - - if ( dev->sg ) - return EINVAL; - - DRM_COPY_FROM_USER_IOCTL(request, (drm_scatter_gather_t *)data, - sizeof(request) ); + int nsegs, ret, i; - entry = malloc(sizeof(*entry), M_DRM, M_WAITOK | M_ZERO); + entry = kmem_zalloc(sizeof(*entry), KM_SLEEP); if ( !entry ) return ENOMEM; - pages = round_page(request.size) / PAGE_SIZE; - DRM_DEBUG( "sg size=%ld pages=%ld\n", request.size, pages ); + pages = round_page(request->size) / PAGE_SIZE; + DRM_DEBUG( "sg size=%ld pages=%ld\n", request->size, pages ); entry->pages = pages; - entry->busaddr = malloc(pages * sizeof(*entry->busaddr), M_DRM, - M_WAITOK | M_ZERO); + entry->busaddr = kmem_zalloc(pages * sizeof(*entry->busaddr), KM_SLEEP); if ( !entry->busaddr ) { - drm_sg_cleanup(entry); + kmem_free(entry, sizeof(*entry)); return ENOMEM; } - entry->handle = (long)malloc(pages << PAGE_SHIFT, M_DRM, - M_WAITOK | M_ZERO); - if (entry->handle == 0) { - drm_sg_cleanup(entry); + dmah = kmem_zalloc(sizeof(struct drm_dma_handle), KM_SLEEP); + if (dmah == NULL) { + kmem_free(entry->busaddr, pages * sizeof(*entry->busaddr)); + kmem_free(entry, sizeof(*entry)); + return ENOMEM; + } + + dmah->dmat = dev->pa.pa_dmat; + + ret = bus_dmamem_alloc(dmah->dmat, request->size, PAGE_SIZE, 0, + dmah->segs, 1, &nsegs, 0); + if (ret != 0) { + kmem_free(dmah, sizeof(struct drm_dma_handle)); + kmem_free(entry->busaddr, pages * sizeof(*entry->busaddr)); + kmem_free(entry, sizeof(*entry)); + return ENOMEM; + } + + ret = bus_dmamem_map(dmah->dmat, dmah->segs, nsegs, request->size, + &dmah->addr, BUS_DMA_COHERENT); + if (ret != 0) { + bus_dmamem_free(dmah->dmat, dmah->segs, nsegs); + kmem_free(dmah, sizeof(struct drm_dma_handle)); + kmem_free(entry->busaddr, pages * sizeof(*entry->busaddr)); + kmem_free(entry, sizeof(*entry)); + return ENOMEM; + } + + ret = bus_dmamap_create(dmah->dmat, request->size, pages, PAGE_SIZE, + 0, 0, &dmah->map); + + if (ret != 0) { + bus_dmamem_unmap(dmah->dmat, dmah->addr, request->size); + bus_dmamem_free(dmah->dmat, dmah->segs, nsegs); + kmem_free(dmah, sizeof(struct drm_dma_handle)); + kmem_free(entry->busaddr, pages * sizeof(*entry->busaddr)); + kmem_free(entry, sizeof(*entry)); + return ENOMEM; + } + + ret = bus_dmamap_load(dmah->dmat, dmah->map, dmah->addr, request->size, + NULL, 0); + if (ret != 0) { + bus_dmamem_unmap(dmah->dmat, dmah->addr, request->size); + bus_dmamem_free(dmah->dmat, dmah->segs, nsegs); + kmem_free(dmah, sizeof(struct drm_dma_handle)); + kmem_free(entry->busaddr, pages * sizeof(*entry->busaddr)); + kmem_free(entry, sizeof(*entry)); return ENOMEM; } - for (i = 0; i < pages; i++) { - entry->busaddr[i] = vtophys(entry->handle + i * PAGE_SIZE); + DRM_DEBUG( "sg alloc nsegs = %d\n", dmah->map->dm_nsegs); + + for (i = 0; i < pages; i++) { + entry->busaddr[i] = dmah->map->dm_segs[i].ds_addr; } + entry->handle = (unsigned long)dmah->addr; + entry->sg_dmah = dmah; + DRM_DEBUG( "sg alloc handle = %08lx\n", entry->handle ); entry->virtual = (void *)entry->handle; - request.handle = entry->handle; - - DRM_COPY_TO_USER_IOCTL( (drm_scatter_gather_t *)data, - request, - sizeof(request) ); + request->handle = entry->handle; DRM_LOCK(); if (dev->sg) { @@ -112,7 +146,47 @@ int drm_sg_alloc(DRM_IOCTL_ARGS) return 0; } -int drm_sg_free(DRM_IOCTL_ARGS) +int +drm_sg_alloc_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_scatter_gather_t request; + int ret; + + if ( dev->sg ) + return EINVAL; + + DRM_DEBUG( "%s\n", __FUNCTION__ ); + + DRM_COPY_FROM_USER_IOCTL(request, (drm_scatter_gather_t *)data, + sizeof(request) ); + + ret = drm_sg_alloc(dev, &request); + if (ret != 0) + return ret; + + DRM_COPY_TO_USER_IOCTL( (drm_scatter_gather_t *)data, + request, + sizeof(request) ); + + return 0; +} + +void +drm_sg_cleanup(drm_sg_mem_t *entry) +{ + struct drm_dma_handle *dmah = entry->sg_dmah; + + bus_dmamap_unload(dmah->dmat, dmah->map); + bus_dmamem_unmap(dmah->dmat, dmah->addr, entry->pages << PAGE_SHIFT); + bus_dmamem_free(dmah->dmat, dmah->segs, 1); + kmem_free(dmah, sizeof(struct drm_dma_handle)); + kmem_free(entry->busaddr, entry->pages * sizeof(*entry->busaddr)); + kmem_free(entry, sizeof(*entry)); +} + +int +drm_sg_free_ioctl(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_scatter_gather_t request; @@ -123,12 +197,14 @@ int drm_sg_free(DRM_IOCTL_ARGS) DRM_LOCK(); entry = dev->sg; + if ( !entry || entry->handle != request.handle ) { + DRM_UNLOCK(); + DRM_DEBUG( "sg free: error: %s handle\n", entry ? "invalid" : "no allocated"); + return EINVAL; + } dev->sg = NULL; DRM_UNLOCK(); - if ( !entry || entry->handle != request.handle ) - return EINVAL; - DRM_DEBUG( "sg free virtual = 0x%lx\n", entry->handle ); drm_sg_cleanup(entry); |
