Home | History | Annotate | Line # | Download | only in bsd-core
      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 (at) 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