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