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
50typedef struct {
51	int used;
52	int context;
53	set_t *sets[2];		/* 0 for video, 1 for AGP */
54} sis_context_t;
55
56static sis_context_t global_ppriv[MAX_CONTEXT];
57
58static 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
71static 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
87static int sis_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
88{
89	return 0;
90}
91
92static 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
120static 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 */
149static 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
172static 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
203static 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
227static 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
250static 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
282static 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
302int 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
333int 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
380drm_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
389int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);
390