1 /*- 2 * Copyright 2003 Eric Anholt. 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 THE 19 * AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23 24 /** 25 * \file drm_pci.h 26 * \brief PCI consistent, DMA-accessible memory allocation. 27 * 28 * \author Eric Anholt <anholt@FreeBSD.org> 29 */ 30 31 #include "drmP.h" 32 33 /**********************************************************************/ 34 /** \name PCI memory */ 35 /*@{*/ 36 37 #if defined(__FreeBSD__) 38 static void 39 drm_pci_busdma_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 40 { 41 drm_dma_handle_t *dmah = arg; 42 43 if (error != 0) 44 return; 45 46 KASSERT(nsegs == 1, ("drm_pci_busdma_callback: bad dma segment count")); 47 dmah->busaddr = segs[0].ds_addr; 48 } 49 #endif 50 51 /** 52 * \brief Allocate a physically contiguous DMA-accessible consistent 53 * memory block. 54 */ 55 drm_dma_handle_t * 56 drm_pci_alloc(struct drm_device *dev, size_t size, 57 size_t align, dma_addr_t maxaddr) 58 { 59 drm_dma_handle_t *dmah; 60 int ret; 61 #if defined (__NetBSD__) 62 int nsegs; 63 #endif 64 65 /* Need power-of-two alignment, so fail the allocation if it isn't. */ 66 if ((align & (align - 1)) != 0) { 67 DRM_ERROR("drm_pci_alloc with non-power-of-two alignment %d\n", 68 (int)align); 69 return NULL; 70 } 71 72 dmah = malloc(sizeof(drm_dma_handle_t), DRM_MEM_DMA, M_ZERO | M_NOWAIT); 73 if (dmah == NULL) 74 return NULL; 75 76 #if defined__FreeBSD__ 77 /* Make sure we aren't holding locks here */ 78 mtx_assert(&dev->dev_lock, MA_NOTOWNED); 79 if (mtx_owned(&dev->dev_lock)) 80 DRM_ERROR("called while holding dev_lock\n"); 81 mtx_assert(&dev->dma_lock, MA_NOTOWNED); 82 if (mtx_owned(&dev->dma_lock)) 83 DRM_ERROR("called while holding dma_lock\n"); 84 85 ret = bus_dma_tag_create(NULL, align, 0, /* tag, align, boundary */ 86 maxaddr, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ 87 NULL, NULL, /* filtfunc, filtfuncargs */ 88 size, 1, size, /* maxsize, nsegs, maxsegsize */ 89 0, NULL, NULL, /* flags, lockfunc, lockfuncargs */ 90 &dmah->tag); 91 if (ret != 0) { 92 free(dmah, DRM_MEM_DMA); 93 return NULL; 94 } 95 96 ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, 97 BUS_DMA_WAITOK | BUS_DMA_ZERO, &dmah->map); 98 if (ret != 0) { 99 bus_dma_tag_destroy(dmah->tag); 100 free(dmah, DRM_MEM_DMA); 101 return NULL; 102 } 103 104 ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, size, 105 drm_pci_busdma_callback, dmah, BUS_DMA_NOWAIT | BUS_DMA_NOCACHE); 106 if (ret != 0) { 107 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 108 bus_dma_tag_destroy(dmah->tag); 109 free(dmah, DRM_MEM_DMA); 110 return NULL; 111 } 112 #elif defined(__NetBSD__) 113 KASSERT(maxaddr >= 0xffffffffUL); /* no way to tell bus_dma_alloc */ 114 dmah->tag = dev->pa.pa_dmat; /* use 32-bit DMA tag */ 115 116 if ((ret = bus_dmamem_alloc(dmah->tag, size, align, 0, 117 dmah->segs, 1, &nsegs, BUS_DMA_WAITOK)) != 0) { 118 printf("drm: Unable to allocate %zu bytes of DMA, error %d\n", 119 size, ret); 120 dmah->tag = NULL; 121 free(dmah, DRM_MEM_DMA); 122 return NULL; 123 } 124 /* XXX is there a better way to deal with this? */ 125 if (nsegs != 1) { 126 printf("drm: bad segment count from bus_dmamem_alloc\n"); 127 bus_dmamem_free(dmah->tag, dmah->segs, 1); 128 dmah->tag = NULL; 129 free(dmah, DRM_MEM_DMA); 130 return NULL; 131 } 132 if ((ret = bus_dmamem_map(dmah->tag, dmah->segs, nsegs, size, 133 &dmah->vaddr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE)) != 0) { 134 printf("drm: Unable to map DMA, error %d\n", ret); 135 bus_dmamem_free(dmah->tag, dmah->segs, 1); 136 dmah->tag = NULL; 137 free(dmah, DRM_MEM_DMA); 138 return NULL; 139 } 140 if ((ret = bus_dmamap_create(dmah->tag, size, 1, size, 0, 141 BUS_DMA_NOWAIT, &dmah->map)) != 0) { 142 printf("drm: Unable to create DMA map, error %d\n", ret); 143 bus_dmamem_unmap(dmah->tag, dmah->vaddr, size); 144 bus_dmamem_free(dmah->tag, dmah->segs, 1); 145 dmah->tag = NULL; 146 free(dmah, DRM_MEM_DMA); 147 return NULL; 148 } 149 if ((ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, 150 size, NULL, BUS_DMA_NOWAIT | BUS_DMA_NOCACHE)) != 0) { 151 printf("drm: Unable to load DMA map, error %d\n", ret); 152 bus_dmamap_destroy(dmah->tag, dmah->map); 153 bus_dmamem_unmap(dmah->tag, dmah->vaddr, size); 154 bus_dmamem_free(dmah->tag, dmah->segs, 1); 155 dmah->tag = NULL; 156 free(dmah, DRM_MEM_DMA); 157 return NULL; 158 } 159 dmah->busaddr = dmah->map->dm_segs[0].ds_addr; 160 dmah->size = size; 161 dmah->nsegs = 1; 162 memset(dmah->vaddr, 0, size); 163 #endif 164 165 return dmah; 166 } 167 168 /** 169 * \brief Free a DMA-accessible consistent memory block. 170 */ 171 void 172 drm_pci_free(struct drm_device *dev, drm_dma_handle_t *dmah) 173 { 174 if (dmah == NULL) 175 return; 176 177 #if defined(__FreeBSD__) 178 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 179 bus_dma_tag_destroy(dmah->tag); 180 #elif defined(__NetBSD__) 181 bus_dmamap_unload(dmah->tag, dmah->map); 182 bus_dmamap_destroy(dmah->tag, dmah->map); 183 bus_dmamem_unmap(dmah->tag, dmah->vaddr, dmah->size); 184 bus_dmamem_free(dmah->tag, dmah->segs, 1); 185 dmah->tag = NULL; 186 #endif 187 188 free(dmah, DRM_MEM_DMA); 189 } 190 191 /*@}*/ 192