xref: /netbsd-src/sys/external/bsd/drm2/drm/drm_memory.c (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1 /*	$NetBSD: drm_memory.c,v 1.13 2018/08/27 15:32:06 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.13 2018/08/27 15:32:06 riastradh Exp $");
34 
35 #if defined(__i386__) || defined(__x86_64__)
36 
37 # ifdef _KERNEL_OPT
38 #  include "agp.h"
39 #  if NAGP > 0
40 #   include "agp_i810.h"
41 #  else
42 #   define NAGP_I810	0
43 #  endif
44 #  include "genfb.h"
45 # else
46 #  define NAGP_I810	1
47 #  define NGENFB	0
48 # endif
49 
50 #else
51 
52 # ifdef _KERNEL_OPT
53 #  define NAGP_I810	0
54 #  include "genfb.h"
55 # else
56 #  define NAGP_I810	0
57 #  define NGENFB	0
58 # endif
59 
60 #endif
61 
62 #include <sys/bus.h>
63 
64 #if NAGP_I810 > 0
65 /* XXX include order botch -- shouldn't need to include pcivar.h */
66 #include <dev/pci/pcivar.h>
67 #include <dev/pci/agpvar.h>
68 #endif
69 
70 #if NGENFB > 0
71 #include <dev/wsfb/genfbvar.h>
72 #endif
73 
74 #include <drm/drmP.h>
75 #include <drm/drm_legacy.h>
76 
77 /*
78  * XXX drm_bus_borrow is a horrible kludge!
79  */
80 static bool
81 drm_bus_borrow(bus_addr_t base, bus_size_t size, bus_space_handle_t *handlep)
82 {
83 
84 #if NAGP_I810 > 0
85 	if (agp_i810_borrow(base, size, handlep))
86 		return true;
87 #endif
88 
89 #if NGENFB > 0
90 	if (genfb_borrow(base, handlep))
91 		return true;
92 #endif
93 
94 	return false;
95 }
96 
97 void
98 drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev)
99 {
100 	const bus_space_tag_t bst = dev->bst;
101 	unsigned int unit;
102 
103 	/*
104 	 * Search dev's bus maps for a match.
105 	 */
106 	for (unit = 0; unit < dev->bus_nmaps; unit++) {
107 		struct drm_bus_map *const bm = &dev->bus_maps[unit];
108 		int flags = bm->bm_flags;
109 
110 		/* Reject maps starting after the request.  */
111 		if (map->offset < bm->bm_base)
112 			continue;
113 
114 		/* Reject maps smaller than the request.  */
115 		if (bm->bm_size < map->size)
116 			continue;
117 
118 		/* Reject maps that the request doesn't fit in.  */
119 		if ((bm->bm_size - map->size) <
120 		    (map->offset - bm->bm_base))
121 			continue;
122 
123 		/* Ensure we can map the space into virtual memory.  */
124 		if (!ISSET(flags, BUS_SPACE_MAP_LINEAR))
125 			continue;
126 
127 		/* Reflect requested flags in the bus_space map.  */
128 		if (ISSET(map->flags, _DRM_WRITE_COMBINING))
129 			flags |= BUS_SPACE_MAP_PREFETCHABLE;
130 
131 		/* Map it.  */
132 		if (bus_space_map(bst, map->offset, map->size, flags,
133 			&map->lm_data.bus_space.bsh))
134 			break;
135 
136 		map->lm_data.bus_space.bus_map = bm;
137 		goto win;
138 	}
139 
140 	/* Couldn't map it.  Try borrowing from someone else.  */
141 	if (drm_bus_borrow(map->offset, map->size,
142 		&map->lm_data.bus_space.bsh)) {
143 		map->lm_data.bus_space.bus_map = NULL;
144 		goto win;
145 	}
146 
147 	/* Failure!  */
148 	return;
149 
150 win:	map->lm_data.bus_space.bst = bst;
151 	map->handle = bus_space_vaddr(bst, map->lm_data.bus_space.bsh);
152 }
153 
154 void
155 drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
156 {
157 	if (map->lm_data.bus_space.bus_map != NULL) {
158 		bus_space_unmap(map->lm_data.bus_space.bst,
159 		    map->lm_data.bus_space.bsh, map->size);
160 		map->lm_data.bus_space.bus_map = NULL;
161 		map->handle = NULL;
162 	}
163 }
164 
165 /*
166  * Allocate a drm dma handle, allocate memory fit for DMA, and map it.
167  *
168  * XXX This is called drm_pci_alloc for hysterical raisins; it is not
169  * specific to PCI.
170  *
171  * XXX For now, we use non-blocking allocations because this is called
172  * by ioctls with the drm global mutex held.
173  *
174  * XXX Error information is lost because this returns NULL on failure,
175  * not even an error embedded in a pointer.
176  */
177 struct drm_dma_handle *
178 drm_pci_alloc(struct drm_device *dev, size_t size, size_t align)
179 {
180 	int nsegs;
181 	int error;
182 
183 	/*
184 	 * Allocate a drm_dma_handle record.
185 	 */
186 	struct drm_dma_handle *const dmah = kmem_alloc(sizeof(*dmah),
187 	    KM_NOSLEEP);
188 	if (dmah == NULL) {
189 		error = -ENOMEM;
190 		goto out;
191 	}
192 	dmah->dmah_tag = dev->dmat;
193 
194 	/*
195 	 * Allocate the requested amount of DMA-safe memory.
196 	 */
197 	/* XXX errno NetBSD->Linux */
198 	error = -bus_dmamem_alloc(dmah->dmah_tag, size, align, 0,
199 	    &dmah->dmah_seg, 1, &nsegs, BUS_DMA_NOWAIT);
200 	if (error)
201 		goto fail0;
202 	KASSERT(nsegs == 1);
203 
204 	/*
205 	 * Map the DMA-safe memory into kernel virtual address space.
206 	 */
207 	/* XXX errno NetBSD->Linux */
208 	error = -bus_dmamem_map(dmah->dmah_tag, &dmah->dmah_seg, 1, size,
209 	    &dmah->vaddr,
210 	    (BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE));
211 	if (error)
212 		goto fail1;
213 	dmah->size = size;
214 
215 	/*
216 	 * Create a map for DMA transfers.
217 	 */
218 	/* XXX errno NetBSD->Linux */
219 	error = -bus_dmamap_create(dmah->dmah_tag, size, 1, size, 0,
220 	    BUS_DMA_NOWAIT, &dmah->dmah_map);
221 	if (error)
222 		goto fail2;
223 
224 	/*
225 	 * Load the kva buffer into the map for DMA transfers.
226 	 */
227 	/* XXX errno NetBSD->Linux */
228 	error = -bus_dmamap_load(dmah->dmah_tag, dmah->dmah_map, dmah->vaddr,
229 	    size, NULL, (BUS_DMA_NOWAIT | BUS_DMA_NOCACHE));
230 	if (error)
231 		goto fail3;
232 
233 	/* Record the bus address for convenient reference.  */
234 	dmah->busaddr = dmah->dmah_map->dm_segs[0].ds_addr;
235 
236 	/* Zero the DMA buffer.  XXX Yikes!  Is this necessary?  */
237 	memset(dmah->vaddr, 0, size);
238 
239 	/* Success!  */
240 	return dmah;
241 
242 fail3:	bus_dmamap_destroy(dmah->dmah_tag, dmah->dmah_map);
243 fail2:	bus_dmamem_unmap(dmah->dmah_tag, dmah->vaddr, dmah->size);
244 fail1:	bus_dmamem_free(dmah->dmah_tag, &dmah->dmah_seg, 1);
245 fail0:	dmah->dmah_tag = NULL;	/* XXX paranoia */
246 	kmem_free(dmah, sizeof(*dmah));
247 out:	DRM_DEBUG("drm_pci_alloc failed: %d\n", error);
248 	return NULL;
249 }
250 
251 /*
252  * Release the bus DMA mappings and memory in dmah, and deallocate it.
253  */
254 void
255 drm_pci_free(struct drm_device *dev, struct drm_dma_handle *dmah)
256 {
257 
258 	bus_dmamap_unload(dmah->dmah_tag, dmah->dmah_map);
259 	bus_dmamap_destroy(dmah->dmah_tag, dmah->dmah_map);
260 	bus_dmamem_unmap(dmah->dmah_tag, dmah->vaddr, dmah->size);
261 	bus_dmamem_free(dmah->dmah_tag, &dmah->dmah_seg, 1);
262 	dmah->dmah_tag = NULL;	/* XXX paranoia */
263 	kmem_free(dmah, sizeof(*dmah));
264 }
265 
266 /*
267  * Make sure the DMA-safe memory allocated for dev lies between
268  * min_addr and max_addr.  Can be used multiple times to restrict the
269  * bounds further, but never to expand the bounds again.
270  *
271  * XXX Caller must guarantee nobody has used the tag yet,
272  * i.e. allocated any DMA memory.
273  */
274 int
275 drm_limit_dma_space(struct drm_device *dev, resource_size_t min_addr,
276     resource_size_t max_addr)
277 {
278 	int ret;
279 
280 	KASSERT(min_addr <= max_addr);
281 
282 	/*
283 	 * Limit it further if we have already limited it, and destroy
284 	 * the old subregion DMA tag.
285 	 */
286 	if (dev->dmat_subregion_p) {
287 		min_addr = MAX(min_addr, dev->dmat_subregion_min);
288 		max_addr = MIN(max_addr, dev->dmat_subregion_max);
289 		bus_dmatag_destroy(dev->dmat);
290 	}
291 
292 	/*
293 	 * If our limit contains the 32-bit space but for some reason
294 	 * we can't use a subregion, either because the bus doesn't
295 	 * support >32-bit DMA or because bus_dma(9) on this platform
296 	 * lacks bus_dmatag_subregion, just use the 32-bit space.
297 	 */
298 	if (min_addr == 0 && max_addr >= UINT32_C(0xffffffff) &&
299 	    dev->bus_dmat == dev->bus_dmat32) {
300 dma32:		dev->dmat = dev->bus_dmat32;
301 		dev->dmat_subregion_p = false;
302 		dev->dmat_subregion_min = 0;
303 		dev->dmat_subregion_max = UINT32_C(0xffffffff);
304 		return 0;
305 	}
306 
307 	/*
308 	 * Create a DMA tag for a subregion from the bus's DMA tag.  If
309 	 * that fails, restore dev->dmat to the whole region so that we
310 	 * need not worry about dev->dmat being uninitialized (not that
311 	 * the caller should try to allocate DMA-safe memory on failure
312 	 * anyway, but...paranoia).
313 	 */
314 	/* XXX errno NetBSD->Linux */
315 	ret = -bus_dmatag_subregion(dev->bus_dmat, min_addr, max_addr,
316 	    &dev->dmat, BUS_DMA_WAITOK);
317 	if (ret) {
318 		/*
319 		 * bus_dmatag_subregion may fail.  If so, and if the
320 		 * subregion contains the 32-bit space, just use the
321 		 * 32-bit DMA tag.
322 		 */
323 		if (ret == -EOPNOTSUPP && dev->bus_dmat32 &&
324 		    min_addr == 0 && max_addr >= UINT32_C(0xffffffff))
325 			goto dma32;
326 		/* XXX Back out?  */
327 		dev->dmat = dev->bus_dmat;
328 		dev->dmat_subregion_p = false;
329 		dev->dmat_subregion_min = 0;
330 		dev->dmat_subregion_max = __type_max(bus_addr_t);
331 		return ret;
332 	}
333 
334 	/*
335 	 * Remember that we have a subregion tag so that we know to
336 	 * destroy it later, and record the bounds in case we need to
337 	 * limit them again.
338 	 */
339 	dev->dmat_subregion_p = true;
340 	dev->dmat_subregion_min = min_addr;
341 	dev->dmat_subregion_max = max_addr;
342 
343 	/* Success!  */
344 	return 0;
345 }
346