2 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
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:
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
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.
25 * Gareth Hughes <gareth@valinux.com>
26 * Eric Anholt <anholt@FreeBSD.org>
30 /** @file drm_scatter.c
31 * Allocation of memory for scatter-gather mappings by the graphics chip.
33 * The memory allocated here is then made into an aperture in the card
34 * by drm_ati_pcigart_init().
39 #if defined(__FreeBSD__)
40 static void drm_sg_alloc_cb(void *arg
, bus_dma_segment_t
*segs
,
41 int nsegs
, int error
);
45 drm_sg_alloc(struct drm_device
*dev
, struct drm_scatter_gather
*request
)
47 struct drm_sg_mem
*entry
;
48 struct drm_dma_handle
*dmah
;
51 #if defined(__NetBSD__)
58 entry
= malloc(sizeof(*entry
), DRM_MEM_SGLISTS
, M_WAITOK
| M_ZERO
);
62 pages
= round_page(request
->size
) / PAGE_SIZE
;
63 DRM_DEBUG("sg size=%ld pages=%ld\n", request
->size
, pages
);
67 entry
->busaddr
= malloc(pages
* sizeof(*entry
->busaddr
), DRM_MEM_PAGES
,
69 if (!entry
->busaddr
) {
70 free(entry
, DRM_MEM_SGLISTS
);
74 #if defined(__FreeBSD__)
75 dmah
= malloc(sizeof(struct drm_dma_handle
), DRM_MEM_DMA
,
78 free(entry
->busaddr
, DRM_MEM_PAGES
);
79 free(entry
, DRM_MEM_SGLISTS
);
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 */
91 free(dmah
, DRM_MEM_DMA
);
92 free(entry
->busaddr
, DRM_MEM_PAGES
);
93 free(entry
, DRM_MEM_SGLISTS
);
97 ret
= bus_dmamem_alloc(dmah
->tag
, &dmah
->vaddr
,
98 BUS_DMA_WAITOK
| BUS_DMA_ZERO
, &dmah
->map
);
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
);
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
);
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
);
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
);
123 free(entry
->busaddr
, DRM_MEM_PAGES
);
124 free(entry
, DRM_MEM_SGLISTS
);
128 dmah
->tag
= dev
->pa
.pa_dmat
;
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",
136 free(dmah
, DRM_MEM_DMA
);
137 free(entry
->busaddr
, DRM_MEM_PAGES
);
138 free(entry
, DRM_MEM_SGLISTS
);
141 DRM_DEBUG("nsegs = %d\n", 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
);
149 free(dmah
, DRM_MEM_DMA
);
150 free(entry
->busaddr
, DRM_MEM_PAGES
);
151 free(entry
, DRM_MEM_SGLISTS
);
154 if ((ret
= bus_dmamap_create(dmah
->tag
, request
->size
, nsegs
,
155 request
->size
, 0, BUS_DMA_NOWAIT
,
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
);
161 free(dmah
, DRM_MEM_DMA
);
162 free(entry
->busaddr
, DRM_MEM_PAGES
);
163 free(entry
, DRM_MEM_SGLISTS
);
166 if ((ret
= bus_dmamap_load(dmah
->tag
, dmah
->map
, dmah
->vaddr
,
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
);
174 free(dmah
, DRM_MEM_DMA
);
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.
182 for (i
= 0, npage
= 0 ; (i
< nsegs
) && (npage
< pages
) ; i
++) {
183 bus_addr_t base
= dmah
->map
->dm_segs
[i
].ds_addr
;
187 (offs
+ PAGE_SIZE
<= dmah
->map
->dm_segs
[i
].ds_len
) &&
190 entry
->busaddr
[npage
++] = base
+ offs
;
193 KASSERT(npage
== pages
);
194 dmah
->size
= request
->size
;
195 memset(dmah
->vaddr
, 0, request
->size
);
198 entry
->sg_dmah
= dmah
;
199 entry
->handle
= (unsigned long)dmah
->vaddr
;
201 DRM_DEBUG("sg alloc handle = %08lx\n", entry
->handle
);
203 entry
->virtual = (void *)entry
->handle
;
204 request
->handle
= entry
->handle
;
209 drm_sg_cleanup(entry
);
218 #if defined(__FreeBSD__)
220 drm_sg_alloc_cb(void *arg
, bus_dma_segment_t
*segs
, int nsegs
, int error
)
222 struct drm_sg_mem
*entry
= arg
;
228 for(i
= 0 ; i
< nsegs
; i
++) {
229 entry
->busaddr
[i
] = segs
[i
].ds_addr
;
235 drm_sg_alloc_ioctl(struct drm_device
*dev
, void *data
,
236 struct drm_file
*file_priv
)
238 struct drm_scatter_gather
*request
= data
;
242 return drm_sg_alloc(dev
, request
);
246 drm_sg_cleanup(struct drm_sg_mem
*entry
)
248 struct drm_dma_handle
*dmah
= entry
->sg_dmah
;
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
);
261 free(dmah
, DRM_MEM_DMA
);
262 free(entry
->busaddr
, DRM_MEM_PAGES
);
263 free(entry
, DRM_MEM_SGLISTS
);
267 drm_sg_free(struct drm_device
*dev
, void *data
, struct drm_file
*file_priv
)
269 struct drm_scatter_gather
*request
= data
;
270 struct drm_sg_mem
*entry
;
277 if (!entry
|| entry
->handle
!= request
->handle
)
280 DRM_DEBUG("sg free virtual = 0x%lx\n", entry
->handle
);
282 drm_sg_cleanup(entry
);