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 (at) valinux.com> 26 * Eric Anholt <anholt (at) 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 "drmP.h" 38 39 #if defined(__FreeBSD__) 40 static void drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs, 41 int nsegs, int error); 42 #endif 43 44 int 45 drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request) 46 { 47 struct drm_sg_mem *entry; 48 struct drm_dma_handle *dmah; 49 unsigned long pages; 50 int ret; 51 #if defined(__NetBSD__) 52 int nsegs, i, npage; 53 #endif 54 55 if (dev->sg) 56 return EINVAL; 57 58 entry = malloc(sizeof(*entry), DRM_MEM_SGLISTS, M_WAITOK | M_ZERO); 59 if (!entry) 60 return ENOMEM; 61 62 pages = round_page(request->size) / PAGE_SIZE; 63 DRM_DEBUG("sg size=%ld pages=%ld\n", request->size, pages); 64 65 entry->pages = pages; 66 67 entry->busaddr = malloc(pages * sizeof(*entry->busaddr), DRM_MEM_PAGES, 68 M_WAITOK | M_ZERO); 69 if (!entry->busaddr) { 70 free(entry, DRM_MEM_SGLISTS); 71 return ENOMEM; 72 } 73 74 #if defined(__FreeBSD__) 75 dmah = malloc(sizeof(struct drm_dma_handle), DRM_MEM_DMA, 76 M_ZERO | M_NOWAIT); 77 if (dmah == NULL) { 78 free(entry->busaddr, DRM_MEM_PAGES); 79 free(entry, DRM_MEM_SGLISTS); 80 return ENOMEM; 81 } 82 83 ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */ 84 BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ 85 NULL, NULL, /* filtfunc, filtfuncargs */ 86 request->size, pages, /* maxsize, nsegs */ 87 PAGE_SIZE, 0, /* maxsegsize, flags */ 88 NULL, NULL, /* lockfunc, lockfuncargs */ 89 &dmah->tag); 90 if (ret != 0) { 91 free(dmah, DRM_MEM_DMA); 92 free(entry->busaddr, DRM_MEM_PAGES); 93 free(entry, DRM_MEM_SGLISTS); 94 return ENOMEM; 95 } 96 97 ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, 98 BUS_DMA_WAITOK | BUS_DMA_ZERO, &dmah->map); 99 if (ret != 0) { 100 bus_dma_tag_destroy(dmah->tag); 101 free(dmah, DRM_MEM_DMA); 102 free(entry->busaddr, DRM_MEM_PAGES); 103 free(entry, DRM_MEM_SGLISTS); 104 return ENOMEM; 105 } 106 107 ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, 108 request->size, drm_sg_alloc_cb, entry, 109 BUS_DMA_NOWAIT | BUS_DMA_NOCACHE); 110 if (ret != 0) { 111 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 112 bus_dma_tag_destroy(dmah->tag); 113 free(dmah, DRM_MEM_DMA); 114 free(entry->busaddr, DRM_MEM_PAGES); 115 free(entry, DRM_MEM_SGLISTS); 116 return ENOMEM; 117 } 118 #elif defined(__NetBSD__) 119 dmah = malloc(sizeof(struct drm_dma_handle) + 120 (pages - 1) * sizeof(bus_dma_segment_t), 121 DRM_MEM_DMA, M_ZERO | M_NOWAIT); 122 if (dmah == NULL) { 123 free(entry->busaddr, DRM_MEM_PAGES); 124 free(entry, DRM_MEM_SGLISTS); 125 return ENOMEM; 126 } 127 128 dmah->tag = dev->pa.pa_dmat; 129 130 if ((ret = bus_dmamem_alloc(dmah->tag, request->size, PAGE_SIZE, 0, 131 dmah->segs, pages, &nsegs, 132 BUS_DMA_WAITOK)) != 0) { 133 printf("drm: Unable to allocate %lu bytes of DMA, error %d\n", 134 request->size, ret); 135 dmah->tag = NULL; 136 free(dmah, DRM_MEM_DMA); 137 free(entry->busaddr, DRM_MEM_PAGES); 138 free(entry, DRM_MEM_SGLISTS); 139 return ENOMEM; 140 } 141 DRM_DEBUG("nsegs = %d\n", nsegs); 142 dmah->nsegs = nsegs; 143 if ((ret = bus_dmamem_map(dmah->tag, dmah->segs, nsegs, request->size, 144 &dmah->vaddr, BUS_DMA_NOWAIT | 145 BUS_DMA_NOCACHE | BUS_DMA_COHERENT)) != 0) { 146 printf("drm: Unable to map DMA, error %d\n", ret); 147 bus_dmamem_free(dmah->tag, dmah->segs, dmah->nsegs); 148 dmah->tag = NULL; 149 free(dmah, DRM_MEM_DMA); 150 free(entry->busaddr, DRM_MEM_PAGES); 151 free(entry, DRM_MEM_SGLISTS); 152 return ENOMEM; 153 } 154 if ((ret = bus_dmamap_create(dmah->tag, request->size, nsegs, 155 request->size, 0, BUS_DMA_NOWAIT, 156 &dmah->map)) != 0) { 157 printf("drm: Unable to create DMA map, error %d\n", ret); 158 bus_dmamem_unmap(dmah->tag, dmah->vaddr, request->size); 159 bus_dmamem_free(dmah->tag, dmah->segs, nsegs); 160 dmah->tag = NULL; 161 free(dmah, DRM_MEM_DMA); 162 free(entry->busaddr, DRM_MEM_PAGES); 163 free(entry, DRM_MEM_SGLISTS); 164 return ENOMEM; 165 } 166 if ((ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, 167 request->size, NULL, 168 BUS_DMA_NOWAIT | BUS_DMA_NOCACHE)) != 0) { 169 printf("drm: Unable to load DMA map, error %d\n", ret); 170 bus_dmamap_destroy(dmah->tag, dmah->map); 171 bus_dmamem_unmap(dmah->tag, dmah->vaddr, request->size); 172 bus_dmamem_free(dmah->tag, dmah->segs, dmah->nsegs); 173 dmah->tag = NULL; 174 free(dmah, DRM_MEM_DMA); 175 return ENOMEM; 176 } 177 /* 178 * We are expected to return address for each page here. 179 * If bus_dmamem_alloc did not return each page in own segment 180 * (likely not), split them as if they were separate segments. 181 */ 182 for (i = 0, npage = 0 ; (i < nsegs) && (npage < pages) ; i++) { 183 bus_addr_t base = dmah->map->dm_segs[i].ds_addr; 184 bus_size_t offs; 185 186 for (offs = 0; 187 (offs + PAGE_SIZE <= dmah->map->dm_segs[i].ds_len) && 188 (npage < pages); 189 offs += PAGE_SIZE) 190 entry->busaddr[npage++] = base + offs; 191 } 192 KASSERT(i == nsegs); 193 KASSERT(npage == pages); 194 dmah->size = request->size; 195 memset(dmah->vaddr, 0, request->size); 196 #endif 197 198 entry->sg_dmah = dmah; 199 entry->handle = (unsigned long)dmah->vaddr; 200 201 DRM_DEBUG("sg alloc handle = %08lx\n", entry->handle); 202 203 entry->virtual = (void *)entry->handle; 204 request->handle = entry->handle; 205 206 DRM_LOCK(); 207 if (dev->sg) { 208 DRM_UNLOCK(); 209 drm_sg_cleanup(entry); 210 return EINVAL; 211 } 212 dev->sg = entry; 213 DRM_UNLOCK(); 214 215 return 0; 216 } 217 218 #if defined(__FreeBSD__) 219 static void 220 drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 221 { 222 struct drm_sg_mem *entry = arg; 223 int i; 224 225 if (error != 0) 226 return; 227 228 for(i = 0 ; i < nsegs ; i++) { 229 entry->busaddr[i] = segs[i].ds_addr; 230 } 231 } 232 #endif 233 234 int 235 drm_sg_alloc_ioctl(struct drm_device *dev, void *data, 236 struct drm_file *file_priv) 237 { 238 struct drm_scatter_gather *request = data; 239 240 DRM_DEBUG("\n"); 241 242 return drm_sg_alloc(dev, request); 243 } 244 245 void 246 drm_sg_cleanup(struct drm_sg_mem *entry) 247 { 248 struct drm_dma_handle *dmah = entry->sg_dmah; 249 250 #if defined(__FreeBSD__) 251 bus_dmamap_unload(dmah->tag, dmah->map); 252 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 253 bus_dma_tag_destroy(dmah->tag); 254 #elif defined(__NetBSD__) 255 bus_dmamap_unload(dmah->tag, dmah->map); 256 bus_dmamap_destroy(dmah->tag, dmah->map); 257 bus_dmamem_unmap(dmah->tag, dmah->vaddr, dmah->size); 258 bus_dmamem_free(dmah->tag, dmah->segs, dmah->nsegs); 259 dmah->tag = NULL; 260 #endif 261 free(dmah, DRM_MEM_DMA); 262 free(entry->busaddr, DRM_MEM_PAGES); 263 free(entry, DRM_MEM_SGLISTS); 264 } 265 266 int 267 drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 268 { 269 struct drm_scatter_gather *request = data; 270 struct drm_sg_mem *entry; 271 272 DRM_LOCK(); 273 entry = dev->sg; 274 dev->sg = NULL; 275 DRM_UNLOCK(); 276 277 if (!entry || entry->handle != request->handle) 278 return EINVAL; 279 280 DRM_DEBUG("sg free virtual = 0x%lx\n", entry->handle); 281 282 drm_sg_cleanup(entry); 283 284 return 0; 285 } 286