fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / char / drm / sis_mm.c
1 /**************************************************************************
2  *
3  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the 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 NON-INFRINGEMENT. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  *
27  **************************************************************************/
28
29 /*
30  * Authors:
31  *    Thomas Hellström <thomas-at-tungstengraphics-dot-com>
32  */
33
34 #include "drmP.h"
35 #include "sis_drm.h"
36 #include "sis_drv.h"
37
38 #include <video/sisfb.h>
39
40 #define VIDEO_TYPE 0
41 #define AGP_TYPE 1
42
43
44 #if defined(CONFIG_FB_SIS)
45 /* fb management via fb device */
46
47 #define SIS_MM_ALIGN_SHIFT 0
48 #define SIS_MM_ALIGN_MASK 0
49
50 static void *sis_sman_mm_allocate(void *private, unsigned long size,
51                                   unsigned alignment)
52 {
53         struct sis_memreq req;
54
55         req.size = size;
56         sis_malloc(&req);
57         if (req.size == 0)
58                 return NULL;
59         else
60                 return (void *)~req.offset;
61 }
62
63 static void sis_sman_mm_free(void *private, void *ref)
64 {
65         sis_free(~((unsigned long)ref));
66 }
67
68 static void sis_sman_mm_destroy(void *private)
69 {
70         ;
71 }
72
73 static unsigned long sis_sman_mm_offset(void *private, void *ref)
74 {
75         return ~((unsigned long)ref);
76 }
77
78 #else /* CONFIG_FB_SIS */
79
80 #define SIS_MM_ALIGN_SHIFT 4
81 #define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1)
82
83 #endif /* CONFIG_FB_SIS */
84
85 static int sis_fb_init(DRM_IOCTL_ARGS)
86 {
87         DRM_DEVICE;
88         drm_sis_private_t *dev_priv = dev->dev_private;
89         drm_sis_fb_t fb;
90         int ret;
91
92         DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb));
93
94         mutex_lock(&dev->struct_mutex);
95 #if defined(CONFIG_FB_SIS)
96         {
97                 drm_sman_mm_t sman_mm;
98                 sman_mm.private = (void *)0xFFFFFFFF;
99                 sman_mm.allocate = sis_sman_mm_allocate;
100                 sman_mm.free = sis_sman_mm_free;
101                 sman_mm.destroy = sis_sman_mm_destroy;
102                 sman_mm.offset = sis_sman_mm_offset;
103                 ret =
104                     drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm);
105         }
106 #else
107         ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0,
108                                  fb.size >> SIS_MM_ALIGN_SHIFT);
109 #endif
110
111         if (ret) {
112                 DRM_ERROR("VRAM memory manager initialisation error\n");
113                 mutex_unlock(&dev->struct_mutex);
114                 return ret;
115         }
116
117         dev_priv->vram_initialized = 1;
118         dev_priv->vram_offset = fb.offset;
119
120         mutex_unlock(&dev->struct_mutex);
121         DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size);
122
123         return 0;
124 }
125
126 static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv,
127                          unsigned long data, int pool)
128 {
129         drm_sis_private_t *dev_priv = dev->dev_private;
130         drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data;
131         drm_sis_mem_t mem;
132         int retval = 0;
133         drm_memblock_item_t *item;
134
135         DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem));
136
137         mutex_lock(&dev->struct_mutex);
138
139         if (0 == ((pool == 0) ? dev_priv->vram_initialized :
140                       dev_priv->agp_initialized)) {
141                 DRM_ERROR
142                     ("Attempt to allocate from uninitialized memory manager.\n");
143                 return DRM_ERR(EINVAL);
144         }
145
146         mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT;
147         item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0,
148                               (unsigned long)priv);
149
150         mutex_unlock(&dev->struct_mutex);
151         if (item) {
152                 mem.offset = ((pool == 0) ?
153                               dev_priv->vram_offset : dev_priv->agp_offset) +
154                     (item->mm->
155                      offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT);
156                 mem.free = item->user_hash.key;
157                 mem.size = mem.size << SIS_MM_ALIGN_SHIFT;
158         } else {
159                 mem.offset = 0;
160                 mem.size = 0;
161                 mem.free = 0;
162                 retval = DRM_ERR(ENOMEM);
163         }
164
165         DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem));
166
167         DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size,
168                   mem.offset);
169
170         return retval;
171 }
172
173 static int sis_drm_free(DRM_IOCTL_ARGS)
174 {
175         DRM_DEVICE;
176         drm_sis_private_t *dev_priv = dev->dev_private;
177         drm_sis_mem_t mem;
178         int ret;
179
180         DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data,
181                                  sizeof(mem));
182
183         mutex_lock(&dev->struct_mutex);
184         ret = drm_sman_free_key(&dev_priv->sman, mem.free);
185         mutex_unlock(&dev->struct_mutex);
186         DRM_DEBUG("free = 0x%lx\n", mem.free);
187
188         return ret;
189 }
190
191 static int sis_fb_alloc(DRM_IOCTL_ARGS)
192 {
193         DRM_DEVICE;
194         return sis_drm_alloc(dev, priv, data, VIDEO_TYPE);
195 }
196
197 static int sis_ioctl_agp_init(DRM_IOCTL_ARGS)
198 {
199         DRM_DEVICE;
200         drm_sis_private_t *dev_priv = dev->dev_private;
201         drm_sis_agp_t agp;
202         int ret;
203         dev_priv = dev->dev_private;
204
205         DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data,
206                                  sizeof(agp));
207         mutex_lock(&dev->struct_mutex);
208         ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0,
209                                  agp.size >> SIS_MM_ALIGN_SHIFT);
210
211         if (ret) {
212                 DRM_ERROR("AGP memory manager initialisation error\n");
213                 mutex_unlock(&dev->struct_mutex);
214                 return ret;
215         }
216
217         dev_priv->agp_initialized = 1;
218         dev_priv->agp_offset = agp.offset;
219         mutex_unlock(&dev->struct_mutex);
220
221         DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size);
222         return 0;
223 }
224
225 static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS)
226 {
227         DRM_DEVICE;
228
229         return sis_drm_alloc(dev, priv, data, AGP_TYPE);
230 }
231
232 static drm_local_map_t *sis_reg_init(drm_device_t *dev)
233 {
234         drm_map_list_t *entry;
235         drm_local_map_t *map;
236
237         list_for_each_entry(entry, &dev->maplist->head, head) {
238                 map = entry->map;
239                 if (!map)
240                         continue;
241                 if (map->type == _DRM_REGISTERS) {
242                         return map;
243                 }
244         }
245         return NULL;
246 }
247
248 int sis_idle(drm_device_t *dev)
249 {
250         drm_sis_private_t *dev_priv = dev->dev_private;
251         uint32_t idle_reg;
252         unsigned long end;
253         int i;
254
255         if (dev_priv->idle_fault)
256                 return 0;
257
258         if (dev_priv->mmio == NULL) {
259                 dev_priv->mmio = sis_reg_init(dev);
260                 if (dev_priv->mmio == NULL) {
261                         DRM_ERROR("Could not find register map.\n");
262                         return 0;
263                 }
264         }
265         
266         /*
267          * Implement a device switch here if needed
268          */
269
270         if (dev_priv->chipset != SIS_CHIP_315)
271                 return 0;
272
273         /*
274          * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here
275          * because its polling frequency is too low.
276          */
277
278         end = jiffies + (DRM_HZ * 3);
279
280         for (i=0; i<4; ++i) {
281                 do {
282                         idle_reg = SIS_READ(0x85cc);
283                 } while ( !time_after_eq(jiffies, end) &&
284                           ((idle_reg & 0x80000000) != 0x80000000));
285         }
286
287         if (time_after_eq(jiffies, end)) {
288                 DRM_ERROR("Graphics engine idle timeout. "
289                           "Disabling idle check\n");
290                 dev_priv->idle_fault = 1;
291         }
292
293         /*
294          * The caller never sees an error code. It gets trapped
295          * in libdrm.
296          */
297
298         return 0;
299 }
300
301
302 void sis_lastclose(struct drm_device *dev)
303 {
304         drm_sis_private_t *dev_priv = dev->dev_private;
305
306         if (!dev_priv)
307                 return;
308
309         mutex_lock(&dev->struct_mutex);
310         drm_sman_cleanup(&dev_priv->sman);
311         dev_priv->vram_initialized = 0;
312         dev_priv->agp_initialized = 0;
313         dev_priv->mmio = NULL;
314         mutex_unlock(&dev->struct_mutex);
315 }
316
317 void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp)
318 {
319         drm_sis_private_t *dev_priv = dev->dev_private;
320         drm_file_t *priv = filp->private_data;
321
322         mutex_lock(&dev->struct_mutex);
323         if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) {
324                 mutex_unlock(&dev->struct_mutex);
325                 return;
326         }
327
328         if (dev->driver->dma_quiescent) {
329                 dev->driver->dma_quiescent(dev);
330         }
331
332         drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv);
333         mutex_unlock(&dev->struct_mutex);
334         return;
335 }
336
337 drm_ioctl_desc_t sis_ioctls[] = {
338         [DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH},
339         [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH},
340         [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] =
341             {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY},
342         [DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH},
343         [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH},
344         [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] =
345             {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY}
346 };
347
348 int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);