Merged mga branch with trunk
[platform/upstream/libdrm.git] / linux / gamma_drv.c
1 /* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
2  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
3  * Revised: Tue Oct 12 08:51:36 1999 by faith@precisioninsight.com
4  *
5  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
6  * All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  * 
15  * The above copyright notice and this permission notice (including the next
16  * paragraph) shall be included in all copies or substantial portions of the
17  * Software.
18  * 
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  * 
27  * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.4 2000/02/23 04:47:28 martin Exp $
28  *
29  */
30
31 #define EXPORT_SYMTAB
32 #include "drmP.h"
33 #include "gamma_drv.h"
34 EXPORT_SYMBOL(gamma_init);
35 EXPORT_SYMBOL(gamma_cleanup);
36
37 #define GAMMA_NAME       "gamma"
38 #define GAMMA_DESC       "3dlabs GMX 2000"
39 #define GAMMA_DATE       "19990830"
40 #define GAMMA_MAJOR      0
41 #define GAMMA_MINOR      0
42 #define GAMMA_PATCHLEVEL 5
43
44 static drm_device_t           gamma_device;
45
46 static struct file_operations gamma_fops = {
47         open:    gamma_open,
48         flush:   drm_flush,
49         release: gamma_release,
50         ioctl:   gamma_ioctl,
51         mmap:    drm_mmap,
52         read:    drm_read,
53         fasync:  drm_fasync,
54         poll:    drm_poll,
55 };
56
57 static struct miscdevice      gamma_misc = {
58         minor: MISC_DYNAMIC_MINOR,
59         name:  GAMMA_NAME,
60         fops:  &gamma_fops,
61 };
62
63 static drm_ioctl_desc_t       gamma_ioctls[] = {
64         [DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { gamma_version,   0, 0 },
65         [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,   0, 0 },
66         [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,    0, 0 },
67         [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,   0, 1 },
68
69         [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,   1, 1 },
70         [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]      = { drm_block,       1, 1 },
71         [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,     1, 1 },
72         [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]    = { gamma_control,   1, 1 },
73         [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,   1, 1 },
74         [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,      1, 1 },
75         [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]   = { drm_addbufs,     1, 1 },
76         [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]  = { drm_markbufs,    1, 1 },
77         [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]  = { drm_infobufs,    1, 0 },
78         [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]   = { drm_mapbufs,     1, 0 },
79         [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]  = { drm_freebufs,    1, 0 },
80         
81         [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { drm_addctx,      1, 1 },
82         [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { drm_rmctx,       1, 1 },
83         [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { drm_modctx,      1, 1 },
84         [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { drm_getctx,      1, 0 },
85         [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx,   1, 1 },
86         [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { drm_newctx,      1, 1 },
87         [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { drm_resctx,      1, 0 },
88         [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,     1, 1 },
89         [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,      1, 1 },
90         [DRM_IOCTL_NR(DRM_IOCTL_DMA)]        = { gamma_dma,       1, 0 },
91         [DRM_IOCTL_NR(DRM_IOCTL_LOCK)]       = { gamma_lock,      1, 0 },
92         [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { gamma_unlock,    1, 0 },
93         [DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,      1, 0 },
94 };
95 #define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls)
96
97 #ifdef MODULE
98 int                           init_module(void);
99 void                          cleanup_module(void);
100 static char                   *gamma = NULL;
101
102 MODULE_AUTHOR("Precision Insight, Inc., Cedar Park, Texas.");
103 MODULE_DESCRIPTION("3dlabs GMX 2000");
104 MODULE_PARM(gamma, "s");
105
106 /* init_module is called when insmod is used to load the module */
107
108 int init_module(void)
109 {
110         return gamma_init();
111 }
112
113 /* cleanup_module is called when rmmod is used to unload the module */
114
115 void cleanup_module(void)
116 {
117         gamma_cleanup();
118 }
119 #endif
120
121 #ifndef MODULE
122 /* gamma_setup is called by the kernel to parse command-line options passed
123  * via the boot-loader (e.g., LILO).  It calls the insmod option routine,
124  * drm_parse_drm.
125  *
126  * This is not currently supported, since it requires changes to
127  * linux/init/main.c. */
128  
129
130 void __init gamma_setup(char *str, int *ints)
131 {
132         if (ints[0] != 0) {
133                 DRM_ERROR("Illegal command line format, ignored\n");
134                 return;
135         }
136         drm_parse_options(str);
137 }
138 #endif
139
140 static int gamma_setup(drm_device_t *dev)
141 {
142         int i;
143         
144         atomic_set(&dev->ioctl_count, 0);
145         atomic_set(&dev->vma_count, 0);
146         dev->buf_use      = 0;
147         atomic_set(&dev->buf_alloc, 0);
148
149         drm_dma_setup(dev);
150
151         atomic_set(&dev->total_open, 0);
152         atomic_set(&dev->total_close, 0);
153         atomic_set(&dev->total_ioctl, 0);
154         atomic_set(&dev->total_irq, 0);
155         atomic_set(&dev->total_ctx, 0);
156         atomic_set(&dev->total_locks, 0);
157         atomic_set(&dev->total_unlocks, 0);
158         atomic_set(&dev->total_contends, 0);
159         atomic_set(&dev->total_sleeps, 0);
160
161         for (i = 0; i < DRM_HASH_SIZE; i++) {
162                 dev->magiclist[i].head = NULL;
163                 dev->magiclist[i].tail = NULL;
164         }
165         dev->maplist        = NULL;
166         dev->map_count      = 0;
167         dev->vmalist        = NULL;
168         dev->lock.hw_lock   = NULL;
169         init_waitqueue_head(&dev->lock.lock_queue);
170         dev->queue_count    = 0;
171         dev->queue_reserved = 0;
172         dev->queue_slots    = 0;
173         dev->queuelist      = NULL;
174         dev->irq            = 0;
175         dev->context_flag   = 0;
176         dev->interrupt_flag = 0;
177         dev->dma_flag       = 0;
178         dev->last_context   = 0;
179         dev->last_switch    = 0;
180         dev->last_checked   = 0;
181         init_timer(&dev->timer);
182         init_waitqueue_head(&dev->context_wait);
183 #if DRM_DMA_HISTO
184         memset(&dev->histo, 0, sizeof(dev->histo));
185 #endif
186         dev->ctx_start      = 0;
187         dev->lck_start      = 0;
188         
189         dev->buf_rp       = dev->buf;
190         dev->buf_wp       = dev->buf;
191         dev->buf_end      = dev->buf + DRM_BSZ;
192         dev->buf_async    = NULL;
193         init_waitqueue_head(&dev->buf_readers);
194         init_waitqueue_head(&dev->buf_writers);
195                         
196         DRM_DEBUG("\n");
197                         
198         /* The kernel's context could be created here, but is now created
199            in drm_dma_enqueue.  This is more resource-efficient for
200            hardware that does not do DMA, but may mean that
201            drm_select_queue fails between the time the interrupt is
202            initialized and the time the queues are initialized. */
203                         
204         return 0;
205 }
206
207
208 static int gamma_takedown(drm_device_t *dev)
209 {
210         int               i;
211         drm_magic_entry_t *pt, *next;
212         drm_map_t         *map;
213         drm_vma_entry_t   *vma, *vma_next;
214
215         DRM_DEBUG("\n");
216
217         if (dev->irq) gamma_irq_uninstall(dev);
218         
219         down(&dev->struct_sem);
220         del_timer(&dev->timer);
221         
222         if (dev->devname) {
223                 drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
224                 dev->devname = NULL;
225         }
226         
227         if (dev->unique) {
228                 drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
229                 dev->unique = NULL;
230                 dev->unique_len = 0;
231         }
232                                 /* Clear pid list */
233         for (i = 0; i < DRM_HASH_SIZE; i++) {
234                 for (pt = dev->magiclist[i].head; pt; pt = next) {
235                         next = pt->next;
236                         drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
237                 }
238                 dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
239         }
240         
241                                 /* Clear vma list (only built for debugging) */
242         if (dev->vmalist) {
243                 for (vma = dev->vmalist; vma; vma = vma_next) {
244                         vma_next = vma->next;
245                         drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
246                 }
247                 dev->vmalist = NULL;
248         }
249         
250                                 /* Clear map area and mtrr information */
251         if (dev->maplist) {
252                 for (i = 0; i < dev->map_count; i++) {
253                         map = dev->maplist[i];
254                         switch (map->type) {
255                         case _DRM_REGISTERS:
256                         case _DRM_FRAME_BUFFER:
257 #ifdef CONFIG_MTRR
258                                 if (map->mtrr >= 0) {
259                                         int retcode;
260                                         retcode = mtrr_del(map->mtrr,
261                                                            map->offset,
262                                                            map->size);
263                                         DRM_DEBUG("mtrr_del = %d\n", retcode);
264                                 }
265 #endif
266                                 drm_ioremapfree(map->handle, map->size);
267                                 break;
268                         case _DRM_SHM:
269                                 drm_free_pages((unsigned long)map->handle,
270                                                drm_order(map->size)
271                                                - PAGE_SHIFT,
272                                                DRM_MEM_SAREA);
273                                 break;
274                         case _DRM_AGP:
275                                 /* Do nothing here, because this is all
276                                    handled in the AGP/GART driver. */
277                                 break;
278                         }
279                         drm_free(map, sizeof(*map), DRM_MEM_MAPS);
280                 }
281                 drm_free(dev->maplist,
282                          dev->map_count * sizeof(*dev->maplist),
283                          DRM_MEM_MAPS);
284                 dev->maplist   = NULL;
285                 dev->map_count = 0;
286         }
287         
288         if (dev->queuelist) {
289                 for (i = 0; i < dev->queue_count; i++) {
290                         drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
291                         if (dev->queuelist[i]) {
292                                 drm_free(dev->queuelist[i],
293                                          sizeof(*dev->queuelist[0]),
294                                          DRM_MEM_QUEUES);
295                                 dev->queuelist[i] = NULL;
296                         }
297                 }
298                 drm_free(dev->queuelist,
299                          dev->queue_slots * sizeof(*dev->queuelist),
300                          DRM_MEM_QUEUES);
301                 dev->queuelist   = NULL;
302         }
303
304         drm_dma_takedown(dev);
305
306         dev->queue_count     = 0;
307         if (dev->lock.hw_lock) {
308                 dev->lock.hw_lock    = NULL; /* SHM removed */
309                 dev->lock.pid        = 0;
310                 wake_up_interruptible(&dev->lock.lock_queue);
311         }
312         up(&dev->struct_sem);
313         
314         return 0;
315 }
316
317 /* gamma_init is called via init_module at module load time, or via
318  * linux/init/main.c (this is not currently supported). */
319
320 int gamma_init(void)
321 {
322         int                   retcode;
323         drm_device_t          *dev = &gamma_device;
324
325         DRM_DEBUG("\n");
326
327         memset((void *)dev, 0, sizeof(*dev));
328         dev->count_lock   = SPIN_LOCK_UNLOCKED;
329         sema_init(&dev->struct_sem, 1);
330         
331 #ifdef MODULE
332         drm_parse_options(gamma);
333 #endif
334
335         if ((retcode = misc_register(&gamma_misc))) {
336                 DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME);
337                 return retcode;
338         }
339         dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor);
340         dev->name   = GAMMA_NAME;
341
342         drm_mem_init();
343         drm_proc_init(dev);
344
345         DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
346                  GAMMA_NAME,
347                  GAMMA_MAJOR,
348                  GAMMA_MINOR,
349                  GAMMA_PATCHLEVEL,
350                  GAMMA_DATE,
351                  gamma_misc.minor);
352         
353         return 0;
354 }
355
356 /* gamma_cleanup is called via cleanup_module at module unload time. */
357
358 void gamma_cleanup(void)
359 {
360         drm_device_t          *dev = &gamma_device;
361
362         DRM_DEBUG("\n");
363         
364         drm_proc_cleanup();
365         if (misc_deregister(&gamma_misc)) {
366                 DRM_ERROR("Cannot unload module\n");
367         } else {
368                 DRM_INFO("Module unloaded\n");
369         }
370         gamma_takedown(dev);
371 }
372
373 int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd,
374                   unsigned long arg)
375 {
376         drm_version_t version;
377         int           len;
378
379         copy_from_user_ret(&version,
380                            (drm_version_t *)arg,
381                            sizeof(version),
382                            -EFAULT);
383
384 #define DRM_COPY(name,value)                                 \
385         len = strlen(value);                                 \
386         if (len > name##_len) len = name##_len;              \
387         name##_len = strlen(value);                          \
388         if (len && name) {                                   \
389                 copy_to_user_ret(name, value, len, -EFAULT); \
390         }
391
392         version.version_major      = GAMMA_MAJOR;
393         version.version_minor      = GAMMA_MINOR;
394         version.version_patchlevel = GAMMA_PATCHLEVEL;
395
396         DRM_COPY(version.name, GAMMA_NAME);
397         DRM_COPY(version.date, GAMMA_DATE);
398         DRM_COPY(version.desc, GAMMA_DESC);
399
400         copy_to_user_ret((drm_version_t *)arg,
401                          &version,
402                          sizeof(version),
403                          -EFAULT);
404         return 0;
405 }
406
407 int gamma_open(struct inode *inode, struct file *filp)
408 {
409         drm_device_t  *dev    = &gamma_device;
410         int           retcode = 0;
411         
412         DRM_DEBUG("open_count = %d\n", dev->open_count);
413         if (!(retcode = drm_open_helper(inode, filp, dev))) {
414                 MOD_INC_USE_COUNT;
415                 atomic_inc(&dev->total_open);
416                 spin_lock(&dev->count_lock);
417                 if (!dev->open_count++) {
418                         spin_unlock(&dev->count_lock);
419                         return gamma_setup(dev);
420                 }
421                 spin_unlock(&dev->count_lock);
422         }
423         return retcode;
424 }
425
426 int gamma_release(struct inode *inode, struct file *filp)
427 {
428         drm_file_t    *priv   = filp->private_data;
429         drm_device_t  *dev    = priv->dev;
430         int           retcode = 0;
431
432         DRM_DEBUG("open_count = %d\n", dev->open_count);
433         if (!(retcode = drm_release(inode, filp))) {
434                 MOD_DEC_USE_COUNT;
435                 atomic_inc(&dev->total_close);
436                 spin_lock(&dev->count_lock);
437                 if (!--dev->open_count) {
438                         if (atomic_read(&dev->ioctl_count) || dev->blocked) {
439                                 DRM_ERROR("Device busy: %d %d\n",
440                                           atomic_read(&dev->ioctl_count),
441                                           dev->blocked);
442                                 spin_unlock(&dev->count_lock);
443                                 return -EBUSY;
444                         }
445                         spin_unlock(&dev->count_lock);
446                         return gamma_takedown(dev);
447                 }
448                 spin_unlock(&dev->count_lock);
449         }
450         return retcode;
451 }
452
453 /* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
454
455 int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
456                 unsigned long arg)
457 {
458         int              nr      = DRM_IOCTL_NR(cmd);
459         drm_file_t       *priv   = filp->private_data;
460         drm_device_t     *dev    = priv->dev;
461         int              retcode = 0;
462         drm_ioctl_desc_t *ioctl;
463         drm_ioctl_t      *func;
464
465         atomic_inc(&dev->ioctl_count);
466         atomic_inc(&dev->total_ioctl);
467         ++priv->ioctl_count;
468         
469         DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
470                   current->pid, cmd, nr, dev->device, priv->authenticated);
471
472         if (nr >= GAMMA_IOCTL_COUNT) {
473                 retcode = -EINVAL;
474         } else {
475                 ioctl     = &gamma_ioctls[nr];
476                 func      = ioctl->func;
477
478                 if (!func) {
479                         DRM_DEBUG("no function\n");
480                         retcode = -EINVAL;
481                 } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
482                             || (ioctl->auth_needed && !priv->authenticated)) {
483                         retcode = -EACCES;
484                 } else {
485                         retcode = (func)(inode, filp, cmd, arg);
486                 }
487         }
488         
489         atomic_dec(&dev->ioctl_count);
490         return retcode;
491 }
492
493
494 int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
495                  unsigned long arg)
496 {
497         drm_file_t        *priv   = filp->private_data;
498         drm_device_t      *dev    = priv->dev;
499         drm_lock_t        lock;
500
501         copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT);
502         
503         if (lock.context == DRM_KERNEL_CONTEXT) {
504                 DRM_ERROR("Process %d using kernel context %d\n",
505                           current->pid, lock.context);
506                 return -EINVAL;
507         }
508
509         DRM_DEBUG("%d frees lock (%d holds)\n",
510                   lock.context,
511                   _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
512         atomic_inc(&dev->total_unlocks);
513         if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
514                 atomic_inc(&dev->total_contends);
515         drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
516         gamma_dma_schedule(dev, 1);
517         if (!dev->context_flag) {
518                 if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
519                                   DRM_KERNEL_CONTEXT)) {
520                         DRM_ERROR("\n");
521                 }
522         }
523 #if DRM_DMA_HISTOGRAM
524         atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
525                                                        - dev->lck_start)]);
526 #endif
527         
528         return 0;
529 }