import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / sgi / char / graphics.c
1 /*
2  * gfx.c: support for SGI's /dev/graphics, /dev/opengl
3  *
4  * Author: Miguel de Icaza (miguel@nuclecu.unam.mx)
5  *         Ralf Baechle (ralf@gnu.org)
6  *         Ulf Carlsson (ulfc@bun.falkenberg.se)
7  *
8  * On IRIX, /dev/graphics is [10, 146]
9  *          /dev/opengl   is [10, 147]
10  *
11  * From a mail with Mark J. Kilgard, /dev/opengl and /dev/graphics are
12  * the same thing, the use of /dev/graphics seems deprecated though.
13  *
14  * The reason that the original SGI programmer had to use only one
15  * device for all the graphic cards on the system will remain a
16  * mistery for the rest of our lives.  Why some ioctls take a board
17  * number and some others not?  Mistery.  Why do they map the hardware
18  * registers into the user address space with an ioctl instead of
19  * mmap?  Mistery too.  Why they did not use the standard way of
20  * making ioctl constants and instead sticked a random constant?
21  * Mistery too.
22  *
23  * We implement those misterious things, and tried not to think about
24  * the reasons behind them.
25  */
26 #include <linux/kernel.h>
27 #include <linux/fs.h>
28 #include <linux/init.h>
29 #include <linux/miscdevice.h>
30 #include <linux/sched.h>
31 #include <linux/mm.h>
32 #include <linux/mman.h>
33 #include <linux/slab.h>
34 #include <linux/module.h>
35 #include <linux/smp_lock.h>
36 #include <asm/uaccess.h>
37 #include "gconsole.h"
38 #include "graphics.h"
39 #include "usema.h"
40 #include <asm/gfx.h>
41 #include <asm/rrm.h>
42 #include <asm/page.h>
43 #include <asm/pgtable.h>
44 #include <video/newport.h>
45
46 #define DEBUG
47
48 /* The boards */
49 extern struct graphics_ops *newport_probe (int, const char **);
50
51 static struct graphics_ops cards [MAXCARDS];
52 static int boards;
53
54 #define GRAPHICS_CARD(inode) 0
55
56 /*
57 void enable_gconsole(void) {};
58 void disable_gconsole(void) {};
59 */
60
61
62 int
63 sgi_graphics_open (struct inode *inode, struct file *file)
64 {
65         struct newport_regs *nregs =
66                 (struct newport_regs *) KSEG1ADDR(cards[0].g_regs);
67
68         newport_wait();
69         nregs->set.wrmask = 0xffffffff;
70         nregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
71                              NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX |
72                              NPORT_DMODE0_STOPY);
73         nregs->set.colori = 1;
74         nregs->set.xystarti = (0 << 16) | 0;
75         nregs->go.xyendi = (1280 << 16) | 1024;
76
77         return 0;
78 }
79
80 int
81 sgi_graphics_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
82 {
83         unsigned int board;
84         unsigned int devnum = GRAPHICS_CARD (inode->i_rdev);
85         int i;
86
87         if ((cmd >= RRM_BASE) && (cmd <= RRM_CMD_LIMIT))
88                 return rrm_command (cmd-RRM_BASE, (void *) arg);
89
90         switch (cmd){
91         case GFX_GETNUM_BOARDS:
92                 return boards;
93
94         case GFX_GETBOARD_INFO: {
95                 struct gfx_getboardinfo_args *bia = (void *) arg;
96                 void   *dest_buf;
97                 int    max_len;
98
99                 i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct gfx_getboardinfo_args));
100                 if (i) return i;
101
102                 if (__get_user (board,    &bia->board) ||
103                     __get_user (dest_buf, &bia->buf) ||
104                     __get_user (max_len,  &bia->len))
105                         return -EFAULT;
106
107                 if (board >= boards)
108                         return -EINVAL;
109                 if (max_len < sizeof (struct gfx_getboardinfo_args))
110                         return -EINVAL;
111                 if (max_len > cards [board].g_board_info_len)
112                         max_len = cards [boards].g_board_info_len;
113                 i = verify_area (VERIFY_WRITE, dest_buf, max_len);
114                 if (i) return i;
115                 if (copy_to_user (dest_buf, cards [board].g_board_info, max_len))
116                         return -EFAULT;
117                 return max_len;
118         }
119
120         case GFX_ATTACH_BOARD: {
121                 struct gfx_attach_board_args *att = (void *) arg;
122                 void *vaddr;
123                 int  r;
124
125                 i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct gfx_attach_board_args));
126                 if (i) return i;
127
128                 if (__get_user (board, &att->board) ||
129                     __get_user (vaddr, &att->vaddr))
130                         return -EFAULT;
131
132                 /* Ok for now we are assuming /dev/graphicsN -> head N even
133                  * if the ioctl api suggests that this is not quite the case.
134                  *
135                  * Otherwise we fail, we use this assumption in the mmap code
136                  * below to find our board information.
137                  */
138                 if (board != devnum){
139                         printk ("Parameter board does not match the current board\n");
140                         return -EINVAL;
141                 }
142
143                 if (board >= boards)
144                         return -EINVAL;
145
146                 /* If it is the first opening it, then make it the board owner */
147                 if (!cards [board].g_owner)
148                         cards [board].g_owner = current;
149
150                 /*
151                  * Ok, we now call mmap on this file, which will end up calling
152                  * sgi_graphics_mmap
153                  */
154                 disable_gconsole ();
155                 down_write(&current->mm->mmap_sem);
156                 r = do_mmap (file, (unsigned long)vaddr,
157                              cards[board].g_regs_size, PROT_READ|PROT_WRITE,
158                              MAP_FIXED|MAP_PRIVATE, 0);
159                 up_write(&current->mm->mmap_sem);
160                 if (r)
161                         return r;
162         }
163
164         /* Strange, the real mapping seems to be done at GFX_ATTACH_BOARD,
165          * GFX_MAPALL is not even used by IRIX X server
166          */
167         case GFX_MAPALL:
168                 return 0;
169
170         case GFX_LABEL:
171                 return 0;
172
173                 /* Version check
174                  * for my IRIX 6.2 X server, this is what the kernel returns
175                  */
176         case 1:
177                 return 3;
178
179         /* Xsgi does not use this one, I assume minor is the board being queried */
180         case GFX_IS_MANAGED:
181                 if (devnum > boards)
182                         return -EINVAL;
183                 return (cards [devnum].g_owner != 0);
184
185         default:
186                 if (cards [devnum].g_ioctl)
187                         return (*cards [devnum].g_ioctl)(devnum, cmd, arg);
188
189         }
190         return -EINVAL;
191 }
192
193 int
194 sgi_graphics_close (struct inode *inode, struct file *file)
195 {
196         int board = GRAPHICS_CARD (inode->i_rdev);
197
198         /* Tell the rendering manager that one client is going away */
199         lock_kernel();
200         rrm_close (inode, file);
201
202         /* Was this file handle from the board owner?, clear it */
203         if (current == cards [board].g_owner){
204                 cards [board].g_owner = 0;
205                 if (cards [board].g_reset_console)
206                         (*cards [board].g_reset_console)();
207                 enable_gconsole ();
208         }
209         unlock_kernel();
210         return 0;
211 }
212
213 /*
214  * This is the core of the direct rendering engine.
215  */
216 struct page *
217 sgi_graphics_nopage (struct vm_area_struct *vma, unsigned long address, int
218                      no_share)
219 {
220         pgd_t *pgd; pmd_t *pmd; pte_t *pte;
221         int board = GRAPHICS_CARD (vma->vm_dentry->d_inode->i_rdev);
222
223         unsigned long virt_add, phys_add;
224
225 #ifdef DEBUG
226         printk ("Got a page fault for board %d address=%lx guser=%lx\n", board,
227                 address, (unsigned long) cards[board].g_user);
228 #endif
229
230         /* Figure out if another process has this mapped, and revoke the mapping
231          * in that case. */
232         if (cards[board].g_user && cards[board].g_user != current) {
233                 /* FIXME: save graphics context here, dump it to rendering
234                  * node? */
235
236                 remove_mapping(cards[board].g_user, vma->vm_start, vma->vm_end);
237         }
238
239         cards [board].g_user = current;
240
241         /* Map the physical address of the newport registers into the address
242          * space of this process */
243
244         virt_add = address & PAGE_MASK;
245         phys_add = cards[board].g_regs + virt_add - vma->vm_start;
246         remap_page_range(virt_add, phys_add, PAGE_SIZE, vma->vm_page_prot);
247
248         pgd = pgd_offset(current->mm, address);
249         pmd = pmd_offset(pgd, address);
250         pte = pte_offset(pmd, address);
251         return pte_page(*pte);
252 }
253
254 /*
255  * We convert a GFX ioctl for mapping hardware registers, in a nice sys_mmap
256  * call, which takes care of everything that must be taken care of.
257  *
258  */
259
260 static struct vm_operations_struct graphics_mmap = {
261         nopage: sgi_graphics_nopage,    /* our magic no-page fault handler */
262 };
263
264 int
265 sgi_graphics_mmap (struct file *file, struct vm_area_struct *vma)
266 {
267         uint size;
268
269         size = vma->vm_end - vma->vm_start;
270
271         /* 1. Set our special graphic virtualizer  */
272         vma->vm_ops = &graphics_mmap;
273
274         /* 2. Set the special tlb permission bits */
275         vma->vm_page_prot = PAGE_USERIO;
276
277         /* final setup */
278         vma->vm_file = file;
279         return 0;
280 }
281
282 #if 0
283 /* Do any post card-detection setup on graphics_ops */
284 static void
285 graphics_ops_post_init (int slot)
286 {
287         /* There is no owner for the card initially */
288         cards [slot].g_owner = (struct task_struct *) 0;
289         cards [slot].g_user  = (struct task_struct *) 0;
290 }
291 #endif
292
293 struct file_operations sgi_graphics_fops = {
294         ioctl:          sgi_graphics_ioctl,
295         mmap:           sgi_graphics_mmap,
296         open:           sgi_graphics_open,
297         release:        sgi_graphics_close,
298 };
299
300 /* /dev/graphics */
301 static struct miscdevice dev_graphics = {
302         SGI_GRAPHICS_MINOR, "sgi-graphics", &sgi_graphics_fops
303 };
304
305 /* /dev/opengl */
306 static struct miscdevice dev_opengl = {
307         SGI_OPENGL_MINOR, "sgi-opengl", &sgi_graphics_fops
308 };
309
310 /* This is called later from the misc-init routine */
311 void __init gfx_register (void)
312 {
313         misc_register (&dev_graphics);
314         misc_register (&dev_opengl);
315 }
316
317 void __init gfx_init (const char **name)
318 {
319 #if 0
320         struct console_ops *console;
321         struct graphics_ops *g;
322 #endif
323
324         printk ("GFX INIT: ");
325         shmiq_init ();
326         usema_init ();
327
328         boards++;
329
330 #if 0
331         if ((g = newport_probe (boards, name)) != 0) {
332                 cards [boards] = *g;
333                 graphics_ops_post_init (boards);
334                 boards++;
335                 console = 0;
336         }
337         /* Add more graphic drivers here */
338         /* Keep passing console around */
339 #endif
340
341         if (boards > MAXCARDS)
342                 printk (KERN_WARNING "Too many cards found on the system\n");
343 }
344
345 #ifdef MODULE
346 MODULE_LICENSE("GPL");
347
348 int init_module(void) {
349         static int initiated = 0;
350
351         printk("SGI Newport Graphics version %i.%i.%i\n",42,54,69);
352
353         if (!initiated++) {
354                 shmiq_init();
355                 usema_init();
356                 printk("Adding first board\n");
357                 boards++;
358                 cards[0].g_regs = 0x1f0f0000;
359                 cards[0].g_regs_size = sizeof (struct newport_regs);
360         }
361
362         printk("Boards: %d\n", boards);
363
364         misc_register (&dev_graphics);
365         misc_register (&dev_opengl);
366
367         return 0;
368 }
369
370 void cleanup_module(void) {
371         printk("Shutting down SGI Newport Graphics\n");
372
373         misc_deregister (&dev_graphics);
374         misc_deregister (&dev_opengl);
375 }
376 #endif