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 /* 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 #include "drmP.h" 37 38 struct drm_sg_dmamem *drm_sg_dmamem_alloc(struct drm_device *, size_t); 39 void drm_sg_dmamem_free(struct drm_sg_dmamem *); 40 41 void 42 drm_sg_cleanup(drm_sg_mem_t *entry) 43 { 44 if (entry != NULL) { 45 if (entry->mem != NULL) 46 drm_sg_dmamem_free(entry->mem); 47 drm_free(entry->busaddr, 48 sizeof(*entry->busaddr) * entry->pages, DRM_MEM_SGLISTS); 49 drm_free(entry, sizeof(entry), DRM_MEM_SGLISTS); 50 } 51 } 52 53 int 54 drm_sg_alloc(struct drm_device * dev, struct drm_scatter_gather *request) 55 { 56 drm_sg_mem_t *entry; 57 unsigned long pages; 58 int i; 59 60 if (dev->sg != NULL) 61 return EINVAL; 62 63 entry = drm_calloc(1, sizeof(*entry), DRM_MEM_SGLISTS); 64 if (entry == NULL) 65 return ENOMEM; 66 67 pages = round_page(request->size) / PAGE_SIZE; 68 DRM_DEBUG("sg size=%ld pages=%ld\n", request->size, pages); 69 70 entry->pages = pages; 71 72 entry->busaddr = drm_calloc(pages, sizeof(*entry->busaddr), 73 DRM_MEM_SGLISTS); 74 if (entry->busaddr == NULL) { 75 drm_sg_cleanup(entry); 76 return ENOMEM; 77 } 78 79 if ((entry->mem = drm_sg_dmamem_alloc(dev, pages)) == NULL) { 80 drm_sg_cleanup(entry); 81 return ENOMEM; 82 } 83 84 entry->handle = (unsigned long)entry->mem->sg_kva; 85 86 for (i = 0; i < pages; i++) 87 entry->busaddr[i] = entry->mem->sg_map->dm_segs[i].ds_addr; 88 89 DRM_DEBUG("sg alloc handle = %08lx\n", entry->handle); 90 91 entry->virtual = (void *)entry->handle; 92 request->handle = entry->handle; 93 94 DRM_LOCK(); 95 if (dev->sg) { 96 DRM_UNLOCK(); 97 drm_sg_cleanup(entry); 98 return EINVAL; 99 } 100 dev->sg = entry; 101 DRM_UNLOCK(); 102 103 return 0; 104 } 105 106 int 107 drm_sg_alloc_ioctl(struct drm_device *dev, void *data, 108 struct drm_file *file_priv) 109 { 110 struct drm_scatter_gather *request = data; 111 int ret; 112 113 DRM_DEBUG("\n"); 114 115 ret = drm_sg_alloc(dev, request); 116 return ret; 117 } 118 119 int 120 drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 121 { 122 struct drm_scatter_gather *request = data; 123 drm_sg_mem_t *entry; 124 125 DRM_LOCK(); 126 entry = dev->sg; 127 dev->sg = NULL; 128 DRM_UNLOCK(); 129 130 if (entry == NULL || entry->handle != request->handle) 131 return EINVAL; 132 133 DRM_DEBUG("sg free virtual = 0x%lx\n", entry->handle); 134 135 drm_sg_cleanup(entry); 136 137 return 0; 138 } 139 140 /* 141 * allocate `pages' pages of dma memory for use in 142 * scatter/gather 143 */ 144 struct drm_sg_dmamem* 145 drm_sg_dmamem_alloc(struct drm_device *dev, size_t pages) 146 { 147 struct drm_sg_dmamem *dsd = NULL; 148 bus_size_t size = pages << PAGE_SHIFT; 149 int ret = 0; 150 151 dsd = drm_calloc(1, sizeof(*dsd), DRM_MEM_SGLISTS); 152 if (dsd == NULL) 153 return (NULL); 154 155 dsd->sg_segs = drm_calloc(pages, sizeof(*dsd->sg_segs), 156 DRM_MEM_SGLISTS); 157 if (dsd->sg_segs == NULL) 158 goto dsdfree; 159 160 dsd->sg_tag = dev->pa.pa_dmat; 161 dsd->sg_size = size; 162 163 if (bus_dmamap_create(dev->pa.pa_dmat, size, pages, PAGE_SIZE, 0, 164 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &dsd->sg_map) != 0) 165 goto segsfree; 166 167 if ((ret = bus_dmamem_alloc(dev->pa.pa_dmat, size, PAGE_SIZE, 0, 168 dsd->sg_segs, pages, &dsd->sg_nsegs, BUS_DMA_NOWAIT)) != 0) 169 goto destroy; 170 171 if (bus_dmamem_map(dev->pa.pa_dmat, dsd->sg_segs, dsd->sg_nsegs, size, 172 &dsd->sg_kva, BUS_DMA_NOWAIT) != 0) 173 goto free; 174 175 if (bus_dmamap_load(dev->pa.pa_dmat, dsd->sg_map, dsd->sg_kva, size, 176 NULL, BUS_DMA_NOWAIT) != 0) 177 goto unmap; 178 179 bzero(dsd->sg_kva, size); 180 181 return (dsd); 182 183 unmap: 184 bus_dmamem_unmap(dev->pa.pa_dmat, dsd->sg_kva, size); 185 free: 186 bus_dmamem_free(dev->pa.pa_dmat, dsd->sg_segs, dsd->sg_nsegs); 187 destroy: 188 bus_dmamap_destroy(dev->pa.pa_dmat, dsd->sg_map); 189 segsfree: 190 drm_free(dsd->sg_segs, sizeof(*dsd->sg_segs) * pages, DRM_MEM_SGLISTS); 191 192 dsdfree: 193 drm_free(dsd, sizeof(*dsd), DRM_MEM_SGLISTS); 194 195 return (NULL); 196 } 197 198 void 199 drm_sg_dmamem_free(struct drm_sg_dmamem *mem) 200 { 201 bus_dmamap_unload(mem->sg_tag, mem->sg_map); 202 bus_dmamem_unmap(mem->sg_tag, mem->sg_kva, mem->sg_size); 203 bus_dmamem_free(mem->sg_tag, mem->sg_segs, mem->sg_nsegs); 204 bus_dmamap_destroy(mem->sg_tag, mem->sg_map); 205 drm_free(mem->sg_segs, sizeof(*mem->sg_segs) * 206 (mem->sg_size >> PAGE_SHIFT), DRM_MEM_SGLISTS); 207 drm_free(mem, sizeof(*mem), DRM_MEM_SGLISTS); 208 } 209