drm_memory.c revision 1.1.2.2 1 /* $NetBSD: drm_memory.c,v 1.1.2.2 2013/07/24 02:39:25 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: drm_memory.c,v 1.1.2.2 2013/07/24 02:39:25 riastradh Exp $");
34
35 /* XXX Cargo-culted from the old drm_memory.c. */
36
37 #ifdef _KERNEL_OPT
38 #include "agp_i810.h"
39 #include "genfb.h"
40 #else
41 #define NAGP_I810 1 /* XXX WTF? */
42 #define NGENFB 0 /* XXX WTF? */
43 #endif
44
45 #include <sys/bus.h>
46
47 #if NAGP_I810 > 0
48 /* XXX include order botch -- shouldn't need to include pcivar.h */
49 #include <dev/pci/pcivar.h>
50 #include <dev/pci/agpvar.h>
51 #endif
52
53 #if NGENFB > 0
54 #include <dev/wsfb/genfbvar.h>
55 #endif
56
57 #include <drm/drmP.h>
58
59 /*
60 * XXX drm_bus_borrow is a horrible kludge!
61 */
62 static bool
63 drm_bus_borrow(bus_addr_t base, bus_space_handle_t *handlep)
64 {
65
66 #if NAGP_I810 > 0
67 if (agp_i810_borrow(base, handlep))
68 return true;
69 #endif
70
71 #if NGENFB > 0
72 if (genfb_borrow(base, handlep))
73 return true;
74 #endif
75
76 return false;
77 }
78
79 void *
80 drm_ioremap(struct drm_device *dev, struct drm_local_map *map)
81 {
82 const bus_space_tag_t bst = dev->bst;
83 unsigned int unit;
84 int error;
85
86 /*
87 * Search dev's bus maps for a match.
88 */
89 for (unit = 0; unit < dev->bus_nmaps; unit++) {
90 struct drm_bus_map *const bm = &dev->bus_maps[unit];
91
92 /* Reject maps starting after the request. */
93 if (map->offset < bm->bm_base)
94 continue;
95
96 /* Reject maps smaller than the request. */
97 if (bm->bm_size < map->size)
98 continue;
99
100 /*
101 * Reject maps that the request doesn't fit in. (Make
102 * sure to avoid integer overflow.)
103 */
104 if ((bm->bm_size - map->size) <
105 (map->offset - bm->bm_base))
106 continue;
107
108 /* Has it been mapped yet? If not, map it. */
109 if (bm->bm_mapped == 0) {
110 KASSERT(ISSET(bm->bm_flags, BUS_SPACE_MAP_LINEAR));
111 error = bus_space_map(bst, bm->bm_base,
112 bm->bm_size, bm->bm_flags, &bm->bm_bsh);
113 if (error) {
114 if (drm_bus_borrow(map->offset, &map->bsh)) {
115 map->bus_map = NULL;
116 goto win;
117 }
118 return NULL;
119 }
120 }
121
122 /* Mark it used and make a subregion just for the request. */
123 bm->bm_mapped++;
124 error = bus_space_subregion(bst, bm->bm_bsh,
125 map->offset - bm->bm_base, map->size, &map->bsh);
126 if (error) {
127 /*
128 * Back out: unmark it and, if nobody else was
129 * using it, unmap it.
130 */
131 if (--bm->bm_mapped == 0)
132 bus_space_unmap(bst, bm->bm_bsh,
133 bm->bm_size);
134 return NULL;
135 }
136
137 /* Got it! */
138 map->bus_map = bm;
139 goto win;
140 }
141
142 /*
143 * No dice. Try mapping it directly ourselves.
144 *
145 * XXX Is this sensible? What prevents us from clobbering some
146 * existing map? And what does this have to do with agp?
147 */
148 for (unit = 0; unit < dev->agp_nmaps; unit++) {
149 struct drm_bus_map *const bm = &dev->agp_maps[unit];
150
151 /* Is this one allocated? */
152 if (bm->bm_mapped > 0) {
153 /*
154 * Make sure it has the same base.
155 *
156 * XXX Why must it be the same base? Can't we
157 * subregion here too?
158 */
159 if (bm->bm_base != map->offset)
160 continue;
161
162 /* Make sure it's big enough. */
163 if (bm->bm_size < map->size)
164 continue;
165
166 /* Mark it used and return it. */
167 bm->bm_mapped++;
168
169 /* XXX size is an input/output parameter too...? */
170 map->size = bm->bm_size;
171
172 map->bsh = bm->bm_bsh;
173 map->bus_map = bm;
174 goto win;
175 } else {
176 const int flags = BUS_SPACE_MAP_PREFETCHABLE |
177 BUS_SPACE_MAP_LINEAR;
178
179 /* Try mapping the request. */
180 error = bus_space_map(bst, map->offset, map->size,
181 flags, &bm->bm_bsh);
182 if (error)
183 return NULL; /* XXX Why not continue? */
184
185 /* Got it. Allocate this bus map. */
186 bm->bm_mapped++;
187 bm->bm_base = map->offset;
188 bm->bm_size = map->size;
189 bm->bm_flags = flags; /* XXX What for? */
190
191 map->bsh = bm->bm_bsh;
192 map->bus_map = bm;
193 goto win;
194 }
195 }
196
197 return NULL;
198
199 win:
200 return bus_space_vaddr(bst, map->bsh);
201 }
202
203 void
204 drm_iounmap(struct drm_device *dev, struct drm_local_map *map)
205 {
206 const bus_space_tag_t bst = dev->bst;
207 struct drm_bus_map *const bm = map->bus_map;
208
209 /*
210 * bm may be null if we have committed the horrible deed of
211 * borrowing from agp_i810 or genfb.
212 */
213 if (bm != NULL) {
214 KASSERT(bm->bm_mapped > 0);
215 if (--bm->bm_mapped)
216 bus_space_unmap(bst, bm->bm_bsh, bm->bm_size);
217 }
218 }
219
220 /*
221 * Allocate a drm dma handle, allocate memory fit for DMA, and map it.
222 *
223 * XXX This is called drm_pci_alloc for hysterical raisins; it is not
224 * specific to PCI.
225 *
226 * XXX For now, we use non-blocking allocations because this is called
227 * by ioctls with the drm global mutex held.
228 *
229 * XXX Error information is lost because this returns NULL on failure,
230 * not even an error embedded in a pointer.
231 */
232 struct drm_dma_handle *
233 drm_pci_alloc(struct drm_device *dev, size_t size, size_t align)
234 {
235 int nsegs;
236 int error;
237
238 /*
239 * Allocate a drm_dma_handle record.
240 *
241 * XXX Must use kzalloc because callers pass this to kfree, not
242 * necessarily to drm_pci_free. Whattakludge.
243 */
244 struct drm_dma_handle *const dmah = kzalloc(sizeof(*dmah), GFP_ATOMIC);
245 if (dmah == NULL) {
246 error = -ENOMEM;
247 goto out;
248 }
249 dmah->dmah_tag = dev->dmat;
250
251 /*
252 * Allocate the requested amount of DMA-safe memory.
253 */
254 /* XXX errno NetBSD->Linux */
255 error = -bus_dmamem_alloc(dmah->dmah_tag, size, align, 0,
256 &dmah->dmah_seg, 1, &nsegs, BUS_DMA_NOWAIT);
257 if (error)
258 goto fail0;
259 KASSERT(nsegs == 1);
260
261 /*
262 * XXX Old drm passed BUS_DMA_NOWAIT below but BUS_DMA_WAITOK
263 * above. WTF?
264 */
265
266 /*
267 * Map the DMA-safe memory into kernel virtual address space.
268 */
269 /* XXX errno NetBSD->Linux */
270 error = -bus_dmamem_map(dmah->dmah_tag, &dmah->dmah_seg, 1, size,
271 &dmah->vaddr,
272 (BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE));
273 if (error)
274 goto fail1;
275 dmah->size = size;
276
277 /*
278 * Create a map for DMA transfers.
279 */
280 /* XXX errno NetBSD->Linux */
281 error = -bus_dmamap_create(dmah->dmah_tag, size, 1, size, 0,
282 BUS_DMA_NOWAIT, &dmah->dmah_map);
283 if (error)
284 goto fail2;
285
286 /*
287 * Load the kva buffer into the map for DMA transfers.
288 */
289 /* XXX errno NetBSD->Linux */
290 error = -bus_dmamap_load(dmah->dmah_tag, dmah->dmah_map, dmah->vaddr,
291 size, NULL, (BUS_DMA_NOWAIT | BUS_DMA_NOCACHE));
292 if (error)
293 goto fail3;
294
295 /* Record the bus address for convenient reference. */
296 dmah->busaddr = dmah->dmah_map->dm_segs[0].ds_addr;
297
298 /* Zero the DMA buffer. XXX Yikes! Is this necessary? */
299 memset(dmah->vaddr, 0, size);
300
301 /* Success! */
302 return dmah;
303
304 fail3: bus_dmamap_destroy(dmah->dmah_tag, dmah->dmah_map);
305 fail2: bus_dmamem_unmap(dmah->dmah_tag, dmah->vaddr, dmah->size);
306 fail1: bus_dmamem_free(dmah->dmah_tag, &dmah->dmah_seg, 1);
307 fail0: dmah->dmah_tag = NULL; /* XXX paranoia */
308 kfree(dmah);
309 out: DRM_DEBUG("drm_pci_alloc failed: %d\n", error);
310 return NULL;
311 }
312
313 /*
314 * Release the bus DMA mappings and memory in dmah.
315 */
316 void
317 __drm_pci_free(struct drm_device *dev, struct drm_dma_handle *dmah)
318 {
319
320 bus_dmamap_unload(dmah->dmah_tag, dmah->dmah_map);
321 bus_dmamap_destroy(dmah->dmah_tag, dmah->dmah_map);
322 bus_dmamem_unmap(dmah->dmah_tag, dmah->vaddr, dmah->size);
323 bus_dmamem_free(dmah->dmah_tag, &dmah->dmah_seg, 1);
324 dmah->dmah_tag = NULL; /* XXX paranoia */
325 }
326
327 /*
328 * Release the bus DMA mappings and memory in dmah, and deallocate it.
329 */
330 void
331 drm_pci_free(struct drm_device *dev, struct drm_dma_handle *dmah)
332 {
333
334 __drm_pci_free(dev, dmah);
335 kfree(dmah);
336 }
337