xref: /openbsd-src/sys/dev/pci/drm/drm_scatter.c (revision 850e275390052b330d93020bf619a739a3c277ac)
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@valinux.com>
26  *   Eric Anholt <anholt@FreeBSD.org>
27  *
28  */
29 
30 /*
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 #include "drmP.h"
37 
38 struct drm_sg_dmamem	*drm_sg_dmamem_alloc(struct drm_device *, size_t);
39 void			 drm_sg_dmamem_free(struct drm_sg_dmamem *);
40 
41 void
42 drm_sg_cleanup(drm_sg_mem_t *entry)
43 {
44 	if (entry != NULL) {
45 		if (entry->mem != NULL)
46 			drm_sg_dmamem_free(entry->mem);
47 		drm_free(entry->busaddr,
48 		    sizeof(*entry->busaddr) * entry->pages, DRM_MEM_SGLISTS);
49 		drm_free(entry, sizeof(entry), DRM_MEM_SGLISTS);
50 	}
51 }
52 
53 int
54 drm_sg_alloc(struct drm_device * dev, struct drm_scatter_gather *request)
55 {
56 	drm_sg_mem_t	*entry;
57 	unsigned long	 pages;
58 	int		 i;
59 
60 	if (dev->sg != NULL)
61 		return EINVAL;
62 
63 	entry = drm_calloc(1, sizeof(*entry), DRM_MEM_SGLISTS);
64         if (entry == NULL)
65                 return ENOMEM;
66 
67 	pages = round_page(request->size) / PAGE_SIZE;
68 	DRM_DEBUG("sg size=%ld pages=%ld\n", request->size, pages);
69 
70 	entry->pages = pages;
71 
72 	entry->busaddr = drm_calloc(pages, sizeof(*entry->busaddr),
73 	    DRM_MEM_SGLISTS);
74 	if (entry->busaddr == NULL) {
75 		drm_sg_cleanup(entry);
76 		return ENOMEM;
77 	}
78 
79 	if ((entry->mem = drm_sg_dmamem_alloc(dev, pages)) == NULL) {
80 		drm_sg_cleanup(entry);
81 		return ENOMEM;
82 	}
83 
84 	entry->handle = (unsigned long)entry->mem->sg_kva;
85 
86 	for (i = 0; i < pages; i++)
87 		entry->busaddr[i] = entry->mem->sg_map->dm_segs[i].ds_addr;
88 
89 	DRM_DEBUG("sg alloc handle  = %08lx\n", entry->handle);
90 
91 	entry->virtual = (void *)entry->handle;
92 	request->handle = entry->handle;
93 
94 	DRM_LOCK();
95 	if (dev->sg) {
96 		DRM_UNLOCK();
97 		drm_sg_cleanup(entry);
98 		return EINVAL;
99 	}
100 	dev->sg = entry;
101 	DRM_UNLOCK();
102 
103 	return 0;
104 }
105 
106 int
107 drm_sg_alloc_ioctl(struct drm_device *dev, void *data,
108     struct drm_file *file_priv)
109 {
110 	struct drm_scatter_gather	*request = data;
111 	int				 ret;
112 
113 	DRM_DEBUG("\n");
114 
115 	ret = drm_sg_alloc(dev, request);
116 	return ret;
117 }
118 
119 int
120 drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
121 {
122 	struct drm_scatter_gather	*request = data;
123 	drm_sg_mem_t			*entry;
124 
125 	DRM_LOCK();
126 	entry = dev->sg;
127 	dev->sg = NULL;
128 	DRM_UNLOCK();
129 
130 	if (entry == NULL || entry->handle != request->handle)
131 		return EINVAL;
132 
133 	DRM_DEBUG("sg free virtual  = 0x%lx\n", entry->handle);
134 
135 	drm_sg_cleanup(entry);
136 
137 	return 0;
138 }
139 
140 /*
141  * allocate `pages' pages of dma memory for use in
142  * scatter/gather
143  */
144 struct drm_sg_dmamem*
145 drm_sg_dmamem_alloc(struct drm_device *dev, size_t pages)
146 {
147 	struct drm_sg_dmamem	*dsd = NULL;
148 	bus_size_t	  	 size = pages << PAGE_SHIFT;
149 	int			 ret = 0;
150 
151 	dsd = drm_calloc(1, sizeof(*dsd), DRM_MEM_SGLISTS);
152 	if (dsd == NULL)
153 		return (NULL);
154 
155 	dsd->sg_segs = drm_calloc(pages, sizeof(*dsd->sg_segs),
156 	    DRM_MEM_SGLISTS);
157 	if (dsd->sg_segs == NULL)
158 		goto dsdfree;
159 
160 	dsd->sg_tag = dev->pa.pa_dmat;
161 	dsd->sg_size = size;
162 
163 	if (bus_dmamap_create(dev->pa.pa_dmat, size, pages, PAGE_SIZE, 0,
164 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &dsd->sg_map) != 0)
165 		goto segsfree;
166 
167 	if ((ret = bus_dmamem_alloc(dev->pa.pa_dmat, size, PAGE_SIZE, 0,
168 	    dsd->sg_segs, pages, &dsd->sg_nsegs, BUS_DMA_NOWAIT)) != 0)
169 		goto destroy;
170 
171 	if (bus_dmamem_map(dev->pa.pa_dmat, dsd->sg_segs, dsd->sg_nsegs, size,
172 	    &dsd->sg_kva, BUS_DMA_NOWAIT) != 0)
173 		goto free;
174 
175 	if (bus_dmamap_load(dev->pa.pa_dmat, dsd->sg_map, dsd->sg_kva, size,
176 	    NULL, BUS_DMA_NOWAIT) != 0)
177 		goto unmap;
178 
179 	bzero(dsd->sg_kva, size);
180 
181 	return (dsd);
182 
183 unmap:
184 	bus_dmamem_unmap(dev->pa.pa_dmat, dsd->sg_kva, size);
185 free:
186 	bus_dmamem_free(dev->pa.pa_dmat, dsd->sg_segs, dsd->sg_nsegs);
187 destroy:
188 	bus_dmamap_destroy(dev->pa.pa_dmat, dsd->sg_map);
189 segsfree:
190 	drm_free(dsd->sg_segs, sizeof(*dsd->sg_segs) * pages, DRM_MEM_SGLISTS);
191 
192 dsdfree:
193 	drm_free(dsd, sizeof(*dsd), DRM_MEM_SGLISTS);
194 
195 	return (NULL);
196 }
197 
198 void
199 drm_sg_dmamem_free(struct drm_sg_dmamem *mem)
200 {
201 	bus_dmamap_unload(mem->sg_tag, mem->sg_map);
202 	bus_dmamem_unmap(mem->sg_tag, mem->sg_kva, mem->sg_size);
203 	bus_dmamem_free(mem->sg_tag, mem->sg_segs, mem->sg_nsegs);
204 	bus_dmamap_destroy(mem->sg_tag, mem->sg_map);
205 	drm_free(mem->sg_segs, sizeof(*mem->sg_segs) *
206 	    (mem->sg_size >> PAGE_SHIFT), DRM_MEM_SGLISTS);
207 	drm_free(mem, sizeof(*mem), DRM_MEM_SGLISTS);
208 }
209