xref: /NextBSD/sys/dev/drm/sis_mm.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /* sis_mm.c -- Private header for Direct Rendering Manager -*- linux-c -*-
2  * Created: Mon Jan  4 10:05:05 1999 by sclin@sis.com.tw
3  *
4  * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
5  * All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
21  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  * Authors:
27  *    Sung-Ching Lin <sclin@sis.com.tw>
28  *
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #if defined(__linux__) && defined(CONFIG_FB_SIS)
35 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
36 #include <video/sisfb.h>
37 #else
38 #include <linux/sisfb.h>
39 #endif
40 #endif
41 #include "dev/drm/drmP.h"
42 #include "dev/drm/sis_drm.h"
43 #include "dev/drm/sis_drv.h"
44 #include "dev/drm/sis_ds.h"
45 
46 #define MAX_CONTEXT 100
47 #define VIDEO_TYPE 0
48 #define AGP_TYPE 1
49 
50 typedef struct {
51 	int used;
52 	int context;
53 	set_t *sets[2];		/* 0 for video, 1 for AGP */
54 } sis_context_t;
55 
56 static sis_context_t global_ppriv[MAX_CONTEXT];
57 
add_alloc_set(int context,int type,unsigned int val)58 static int add_alloc_set(int context, int type, unsigned int val)
59 {
60 	int i, retval = 0;
61 
62 	for (i = 0; i < MAX_CONTEXT; i++) {
63 		if (global_ppriv[i].used && global_ppriv[i].context == context) {
64 			retval = setAdd(global_ppriv[i].sets[type], val);
65 			break;
66 		}
67 	}
68 	return retval;
69 }
70 
del_alloc_set(int context,int type,unsigned int val)71 static int del_alloc_set(int context, int type, unsigned int val)
72 {
73 	int i, retval = 0;
74 
75 	for (i = 0; i < MAX_CONTEXT; i++) {
76 		if (global_ppriv[i].used && global_ppriv[i].context == context) {
77 			retval = setDel(global_ppriv[i].sets[type], val);
78 			break;
79 		}
80 	}
81 	return retval;
82 }
83 
84 /* fb management via fb device */
85 #if defined(__linux__) && defined(CONFIG_FB_SIS)
86 
sis_fb_init(struct drm_device * dev,void * data,struct drm_file * file_priv)87 static int sis_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
88 {
89 	return 0;
90 }
91 
sis_fb_alloc(struct drm_device * dev,void * data,struct drm_file * file_priv)92 static int sis_fb_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv)
93 {
94 	drm_sis_mem_t *fb = data;
95 	struct sis_memreq req;
96 	int retval = 0;
97 
98 	req.size = fb->size;
99 	sis_malloc(&req);
100 	if (req.offset) {
101 		/* TODO */
102 		fb->offset = req.offset;
103 		fb->free = req.offset;
104 		if (!add_alloc_set(fb->context, VIDEO_TYPE, fb->free)) {
105 			DRM_DEBUG("adding to allocation set fails\n");
106 			sis_free(req.offset);
107 			retval = -EINVAL;
108 		}
109 	} else {
110 		fb->offset = 0;
111 		fb->size = 0;
112 		fb->free = 0;
113 	}
114 
115 	DRM_DEBUG("alloc fb, size = %d, offset = %ld\n", fb->size, req.offset);
116 
117 	return retval;
118 }
119 
sis_fb_free(struct drm_device * dev,void * data,struct drm_file * file_priv)120 static int sis_fb_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
121 {
122 	drm_sis_mem_t fb;
123 	int retval = 0;
124 
125 	if (!fb->free)
126 		return -EINVAL;
127 
128 	if (!del_alloc_set(fb->context, VIDEO_TYPE, fb->free))
129 		retval = -EINVAL;
130 	sis_free(fb->free);
131 
132 	DRM_DEBUG("free fb, offset = 0x%lx\n", fb->free);
133 
134 	return retval;
135 }
136 
137 #else
138 
139 /* Called by the X Server to initialize the FB heap.  Allocations will fail
140  * unless this is called.  Offset is the beginning of the heap from the
141  * framebuffer offset (MaxXFBMem in XFree86).
142  *
143  * Memory layout according to Thomas Winischofer:
144  * |------------------|DDDDDDDDDDDDDDDDDDDDDDDDDDDDD|HHHH|CCCCCCCCCCC|
145  *
146  *    X driver/sisfb                                  HW-   Command-
147  *  framebuffer memory           DRI heap           Cursor   queue
148  */
sis_fb_init(struct drm_device * dev,void * data,struct drm_file * file_priv)149 static int sis_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
150 {
151 	drm_sis_private_t *dev_priv = dev->dev_private;
152 	drm_sis_fb_t *fb = data;
153 
154 	if (dev_priv == NULL) {
155 		dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t),
156 					      DRM_MEM_DRIVER);
157 		dev_priv = dev->dev_private;
158 		if (dev_priv == NULL)
159 			return ENOMEM;
160 	}
161 
162 	if (dev_priv->FBHeap != NULL)
163 		return -EINVAL;
164 
165 	dev_priv->FBHeap = mmInit(fb->offset, fb->size);
166 
167 	DRM_DEBUG("offset = %u, size = %u", fb->offset, fb->size);
168 
169 	return 0;
170 }
171 
sis_fb_alloc(struct drm_device * dev,void * data,struct drm_file * file_priv)172 static int sis_fb_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv)
173 {
174 	drm_sis_private_t *dev_priv = dev->dev_private;
175 	drm_sis_mem_t *fb = data;
176 	PMemBlock block;
177 	int retval = 0;
178 
179 	if (dev_priv == NULL || dev_priv->FBHeap == NULL)
180 		return -EINVAL;
181 
182 	block = mmAllocMem(dev_priv->FBHeap, fb->size, 0, 0);
183 	if (block) {
184 		/* TODO */
185 		fb->offset = block->ofs;
186 		fb->free = (unsigned long)block;
187 		if (!add_alloc_set(fb->context, VIDEO_TYPE, fb->free)) {
188 			DRM_DEBUG("adding to allocation set fails\n");
189 			mmFreeMem((PMemBlock) fb->free);
190 			retval = -EINVAL;
191 		}
192 	} else {
193 		fb->offset = 0;
194 		fb->size = 0;
195 		fb->free = 0;
196 	}
197 
198 	DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb->size, fb->offset);
199 
200 	return retval;
201 }
202 
sis_fb_free(struct drm_device * dev,void * data,struct drm_file * file_priv)203 static int sis_fb_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
204 {
205 	drm_sis_private_t *dev_priv = dev->dev_private;
206 	drm_sis_mem_t *fb = data;
207 
208 	if (dev_priv == NULL || dev_priv->FBHeap == NULL)
209 		return -EINVAL;
210 
211 	if (!mmBlockInHeap(dev_priv->FBHeap, (PMemBlock) fb->free))
212 		return -EINVAL;
213 
214 	if (!del_alloc_set(fb->context, VIDEO_TYPE, fb->free))
215 		return -EINVAL;
216 	mmFreeMem((PMemBlock) fb->free);
217 
218 	DRM_DEBUG("free fb, free = 0x%lx\n", fb->free);
219 
220 	return 0;
221 }
222 
223 #endif
224 
225 /* agp memory management */
226 
sis_ioctl_agp_init(struct drm_device * dev,void * data,struct drm_file * file_priv)227 static int sis_ioctl_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
228 {
229 	drm_sis_private_t *dev_priv = dev->dev_private;
230 	drm_sis_agp_t *agp = data;
231 
232 	if (dev_priv == NULL) {
233 		dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t),
234 					      DRM_MEM_DRIVER);
235 		dev_priv = dev->dev_private;
236 		if (dev_priv == NULL)
237 			return ENOMEM;
238 	}
239 
240 	if (dev_priv->AGPHeap != NULL)
241 		return -EINVAL;
242 
243 	dev_priv->AGPHeap = mmInit(agp->offset, agp->size);
244 
245 	DRM_DEBUG("offset = %u, size = %u", agp->offset, agp->size);
246 
247 	return 0;
248 }
249 
sis_ioctl_agp_alloc(struct drm_device * dev,void * data,struct drm_file * file_priv)250 static int sis_ioctl_agp_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv)
251 {
252 	drm_sis_private_t *dev_priv = dev->dev_private;
253 	drm_sis_mem_t *agp = data;
254 	PMemBlock block;
255 	int retval = 0;
256 
257 	if (dev_priv == NULL || dev_priv->AGPHeap == NULL)
258 		return -EINVAL;
259 
260 	block = mmAllocMem(dev_priv->AGPHeap, agp->size, 0, 0);
261 	if (block) {
262 		/* TODO */
263 		agp->offset = block->ofs;
264 		agp->free = (unsigned long)block;
265 		if (!add_alloc_set(agp->context, AGP_TYPE, agp->free)) {
266 			DRM_DEBUG("adding to allocation set fails\n");
267 			mmFreeMem((PMemBlock) agp->free);
268 			retval = -1;
269 		}
270 	} else {
271 		agp->offset = 0;
272 		agp->size = 0;
273 		agp->free = 0;
274 	}
275 
276 	DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp->size,
277 	    agp->offset);
278 
279 	return retval;
280 }
281 
sis_ioctl_agp_free(struct drm_device * dev,void * data,struct drm_file * file_priv)282 static int sis_ioctl_agp_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
283 {
284 	drm_sis_private_t *dev_priv = dev->dev_private;
285 	drm_sis_mem_t *agp = data;
286 
287 	if (dev_priv == NULL || dev_priv->AGPHeap == NULL)
288 		return -EINVAL;
289 
290 	if (!mmBlockInHeap(dev_priv->AGPHeap, (PMemBlock) agp->free))
291 		return -EINVAL;
292 
293 	mmFreeMem((PMemBlock) agp->free);
294 	if (!del_alloc_set(agp->context, AGP_TYPE, agp->free))
295 		return -EINVAL;
296 
297 	DRM_DEBUG("free agp, free = 0x%lx\n", agp->free);
298 
299 	return 0;
300 }
301 
sis_init_context(struct drm_device * dev,int context)302 int sis_init_context(struct drm_device *dev, int context)
303 {
304 	int i;
305 
306 	for (i = 0; i < MAX_CONTEXT; i++) {
307 		if (global_ppriv[i].used &&
308 		    (global_ppriv[i].context == context))
309 			break;
310 	}
311 
312 	if (i >= MAX_CONTEXT) {
313 		for (i = 0; i < MAX_CONTEXT; i++) {
314 			if (!global_ppriv[i].used) {
315 				global_ppriv[i].context = context;
316 				global_ppriv[i].used = 1;
317 				global_ppriv[i].sets[0] = setInit();
318 				global_ppriv[i].sets[1] = setInit();
319 				DRM_DEBUG("init allocation set, socket=%d, "
320 					  "context = %d\n", i, context);
321 				break;
322 			}
323 		}
324 		if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) ||
325 		    (global_ppriv[i].sets[1] == NULL)) {
326 			return 0;
327 		}
328 	}
329 
330 	return 1;
331 }
332 
sis_final_context(struct drm_device * dev,int context)333 int sis_final_context(struct drm_device *dev, int context)
334 {
335 	int i;
336 
337 	for (i = 0; i < MAX_CONTEXT; i++) {
338 		if (global_ppriv[i].used &&
339 		    (global_ppriv[i].context == context))
340 			break;
341 	}
342 
343 	if (i < MAX_CONTEXT) {
344 		set_t *set;
345 		ITEM_TYPE item;
346 		int retval;
347 
348 		DRM_DEBUG("find socket %d, context = %d\n", i, context);
349 
350 		/* Video Memory */
351 		set = global_ppriv[i].sets[0];
352 		retval = setFirst(set, &item);
353 		while (retval) {
354 			DRM_DEBUG("free video memory 0x%lx\n", item);
355 #if defined(__linux__) && defined(CONFIG_FB_SIS)
356 			sis_free(item);
357 #else
358 			mmFreeMem((PMemBlock) item);
359 #endif
360 			retval = setNext(set, &item);
361 		}
362 		setDestroy(set);
363 
364 		/* AGP Memory */
365 		set = global_ppriv[i].sets[1];
366 		retval = setFirst(set, &item);
367 		while (retval) {
368 			DRM_DEBUG("free agp memory 0x%lx\n", item);
369 			mmFreeMem((PMemBlock) item);
370 			retval = setNext(set, &item);
371 		}
372 		setDestroy(set);
373 
374 		global_ppriv[i].used = 0;
375 	}
376 
377 	return 1;
378 }
379 
380 drm_ioctl_desc_t sis_ioctls[] = {
381 	DRM_IOCTL_DEF(DRM_SIS_FB_ALLOC, sis_fb_alloc, DRM_AUTH),
382 	DRM_IOCTL_DEF(DRM_SIS_FB_FREE, sis_fb_free, DRM_AUTH),
383 	DRM_IOCTL_DEF(DRM_SIS_AGP_INIT, sis_ioctl_agp_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
384 	DRM_IOCTL_DEF(DRM_SIS_AGP_ALLOC, sis_ioctl_agp_alloc, DRM_AUTH),
385 	DRM_IOCTL_DEF(DRM_SIS_AGP_FREE, sis_ioctl_agp_free, DRM_AUTH),
386 	DRM_IOCTL_DEF(DRM_SIS_FB_INIT, sis_fb_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY)
387 };
388 
389 int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);
390