1 /*- 2 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Gareth Hughes <gareth@valinux.com> 26 * Eric Anholt <anholt@FreeBSD.org> 27 * 28 */ 29 30 /** @file drm_scatter.c 31 * Allocation of memory for scatter-gather mappings by the graphics chip. 32 * 33 * The memory allocated here is then made into an aperture in the card 34 * by drm_ati_pcigart_init(). 35 */ 36 37 #include "dev/drm/drmP.h" 38 39 static void drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs, 40 int nsegs, int error); 41 42 int 43 drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request) 44 { 45 struct drm_sg_mem *entry; 46 struct drm_dma_handle *dmah; 47 unsigned long pages; 48 int ret; 49 50 if (dev->sg) 51 return EINVAL; 52 53 entry = malloc(sizeof(*entry), DRM_MEM_SGLISTS, M_WAITOK | M_ZERO); 54 if (!entry) 55 return ENOMEM; 56 57 pages = round_page(request->size) / PAGE_SIZE; 58 DRM_DEBUG("sg size=%ld pages=%ld\n", request->size, pages); 59 60 entry->pages = pages; 61 62 entry->busaddr = malloc(pages * sizeof(*entry->busaddr), DRM_MEM_PAGES, 63 M_WAITOK | M_ZERO); 64 if (!entry->busaddr) { 65 free(entry, DRM_MEM_SGLISTS); 66 return ENOMEM; 67 } 68 69 dmah = malloc(sizeof(struct drm_dma_handle), DRM_MEM_DMA, 70 M_ZERO | M_NOWAIT); 71 if (dmah == NULL) { 72 free(entry->busaddr, DRM_MEM_PAGES); 73 free(entry, DRM_MEM_SGLISTS); 74 return ENOMEM; 75 } 76 77 ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */ 78 BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ 79 NULL, NULL, /* filtfunc, filtfuncargs */ 80 request->size, pages, /* maxsize, nsegs */ 81 PAGE_SIZE, 0, /* maxsegsize, flags */ 82 &dmah->tag); 83 if (ret != 0) { 84 free(dmah, DRM_MEM_DMA); 85 free(entry->busaddr, DRM_MEM_PAGES); 86 free(entry, DRM_MEM_SGLISTS); 87 return ENOMEM; 88 } 89 90 /* XXX BUS_DMA_NOCACHE */ 91 ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, 92 BUS_DMA_WAITOK | BUS_DMA_ZERO , &dmah->map); 93 if (ret != 0) { 94 bus_dma_tag_destroy(dmah->tag); 95 free(dmah, DRM_MEM_DMA); 96 free(entry->busaddr, DRM_MEM_PAGES); 97 free(entry, DRM_MEM_SGLISTS); 98 return ENOMEM; 99 } 100 101 ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, 102 request->size, drm_sg_alloc_cb, entry, BUS_DMA_NOWAIT); 103 if (ret != 0) { 104 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 105 bus_dma_tag_destroy(dmah->tag); 106 free(dmah, DRM_MEM_DMA); 107 free(entry->busaddr, DRM_MEM_PAGES); 108 free(entry, DRM_MEM_SGLISTS); 109 return ENOMEM; 110 } 111 112 entry->dmah = dmah; 113 entry->handle = (unsigned long)dmah->vaddr; 114 115 DRM_DEBUG("sg alloc handle = %08lx\n", entry->handle); 116 117 entry->virtual = (void *)entry->handle; 118 request->handle = entry->handle; 119 120 DRM_LOCK(); 121 if (dev->sg) { 122 DRM_UNLOCK(); 123 drm_sg_cleanup(entry); 124 return EINVAL; 125 } 126 dev->sg = entry; 127 DRM_UNLOCK(); 128 129 return 0; 130 } 131 132 static void 133 drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 134 { 135 struct drm_sg_mem *entry = arg; 136 int i; 137 138 if (error != 0) 139 return; 140 141 for(i = 0 ; i < nsegs ; i++) { 142 entry->busaddr[i] = segs[i].ds_addr; 143 } 144 } 145 146 int 147 drm_sg_alloc_ioctl(struct drm_device *dev, void *data, 148 struct drm_file *file_priv) 149 { 150 struct drm_scatter_gather *request = data; 151 152 DRM_DEBUG("\n"); 153 154 return drm_sg_alloc(dev, request); 155 } 156 157 void 158 drm_sg_cleanup(struct drm_sg_mem *entry) 159 { 160 struct drm_dma_handle *dmah = entry->dmah; 161 162 bus_dmamap_unload(dmah->tag, dmah->map); 163 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 164 bus_dma_tag_destroy(dmah->tag); 165 free(dmah, DRM_MEM_DMA); 166 free(entry->busaddr, DRM_MEM_PAGES); 167 free(entry, DRM_MEM_SGLISTS); 168 } 169 170 int 171 drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 172 { 173 struct drm_scatter_gather *request = data; 174 struct drm_sg_mem *entry; 175 176 DRM_LOCK(); 177 entry = dev->sg; 178 dev->sg = NULL; 179 DRM_UNLOCK(); 180 181 if (!entry || entry->handle != request->handle) 182 return EINVAL; 183 184 DRM_DEBUG("sg free virtual = 0x%lx\n", entry->handle); 185 186 drm_sg_cleanup(entry); 187 188 return 0; 189 } 190