2 * gfx.c: support for SGI's /dev/graphics, /dev/opengl
4 * Author: Miguel de Icaza (miguel@nuclecu.unam.mx)
5 * Ralf Baechle (ralf@gnu.org)
6 * Ulf Carlsson (ulfc@bun.falkenberg.se)
8 * On IRIX, /dev/graphics is [10, 146]
9 * /dev/opengl is [10, 147]
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.
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?
23 * We implement those misterious things, and tried not to think about
24 * the reasons behind them.
26 #include <linux/kernel.h>
28 #include <linux/init.h>
29 #include <linux/miscdevice.h>
30 #include <linux/sched.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>
43 #include <asm/pgtable.h>
44 #include <video/newport.h>
49 extern struct graphics_ops *newport_probe (int, const char **);
51 static struct graphics_ops cards [MAXCARDS];
54 #define GRAPHICS_CARD(inode) 0
57 void enable_gconsole(void) {};
58 void disable_gconsole(void) {};
63 sgi_graphics_open (struct inode *inode, struct file *file)
65 struct newport_regs *nregs =
66 (struct newport_regs *) KSEG1ADDR(cards[0].g_regs);
69 nregs->set.wrmask = 0xffffffff;
70 nregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
71 NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX |
73 nregs->set.colori = 1;
74 nregs->set.xystarti = (0 << 16) | 0;
75 nregs->go.xyendi = (1280 << 16) | 1024;
81 sgi_graphics_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
84 unsigned int devnum = GRAPHICS_CARD (inode->i_rdev);
87 if ((cmd >= RRM_BASE) && (cmd <= RRM_CMD_LIMIT))
88 return rrm_command (cmd-RRM_BASE, (void *) arg);
91 case GFX_GETNUM_BOARDS:
94 case GFX_GETBOARD_INFO: {
95 struct gfx_getboardinfo_args *bia = (void *) arg;
99 i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct gfx_getboardinfo_args));
102 if (__get_user (board, &bia->board) ||
103 __get_user (dest_buf, &bia->buf) ||
104 __get_user (max_len, &bia->len))
109 if (max_len < sizeof (struct gfx_getboardinfo_args))
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);
115 if (copy_to_user (dest_buf, cards [board].g_board_info, max_len))
120 case GFX_ATTACH_BOARD: {
121 struct gfx_attach_board_args *att = (void *) arg;
125 i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct gfx_attach_board_args));
128 if (__get_user (board, &att->board) ||
129 __get_user (vaddr, &att->vaddr))
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.
135 * Otherwise we fail, we use this assumption in the mmap code
136 * below to find our board information.
138 if (board != devnum){
139 printk ("Parameter board does not match the current board\n");
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;
151 * Ok, we now call mmap on this file, which will end up calling
155 down_write(¤t->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(¤t->mm->mmap_sem);
164 /* Strange, the real mapping seems to be done at GFX_ATTACH_BOARD,
165 * GFX_MAPALL is not even used by IRIX X server
174 * for my IRIX 6.2 X server, this is what the kernel returns
179 /* Xsgi does not use this one, I assume minor is the board being queried */
183 return (cards [devnum].g_owner != 0);
186 if (cards [devnum].g_ioctl)
187 return (*cards [devnum].g_ioctl)(devnum, cmd, arg);
194 sgi_graphics_close (struct inode *inode, struct file *file)
196 int board = GRAPHICS_CARD (inode->i_rdev);
198 /* Tell the rendering manager that one client is going away */
200 rrm_close (inode, file);
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)();
214 * This is the core of the direct rendering engine.
217 sgi_graphics_nopage (struct vm_area_struct *vma, unsigned long address, int
220 pgd_t *pgd; pmd_t *pmd; pte_t *pte;
221 int board = GRAPHICS_CARD (vma->vm_dentry->d_inode->i_rdev);
223 unsigned long virt_add, phys_add;
226 printk ("Got a page fault for board %d address=%lx guser=%lx\n", board,
227 address, (unsigned long) cards[board].g_user);
230 /* Figure out if another process has this mapped, and revoke the mapping
232 if (cards[board].g_user && cards[board].g_user != current) {
233 /* FIXME: save graphics context here, dump it to rendering
236 remove_mapping(cards[board].g_user, vma->vm_start, vma->vm_end);
239 cards [board].g_user = current;
241 /* Map the physical address of the newport registers into the address
242 * space of this process */
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);
248 pgd = pgd_offset(current->mm, address);
249 pmd = pmd_offset(pgd, address);
250 pte = pte_offset(pmd, address);
251 return pte_page(*pte);
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.
260 static struct vm_operations_struct graphics_mmap = {
261 nopage: sgi_graphics_nopage, /* our magic no-page fault handler */
265 sgi_graphics_mmap (struct file *file, struct vm_area_struct *vma)
269 size = vma->vm_end - vma->vm_start;
271 /* 1. Set our special graphic virtualizer */
272 vma->vm_ops = &graphics_mmap;
274 /* 2. Set the special tlb permission bits */
275 vma->vm_page_prot = PAGE_USERIO;
283 /* Do any post card-detection setup on graphics_ops */
285 graphics_ops_post_init (int slot)
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;
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,
301 static struct miscdevice dev_graphics = {
302 SGI_GRAPHICS_MINOR, "sgi-graphics", &sgi_graphics_fops
306 static struct miscdevice dev_opengl = {
307 SGI_OPENGL_MINOR, "sgi-opengl", &sgi_graphics_fops
310 /* This is called later from the misc-init routine */
311 void __init gfx_register (void)
313 misc_register (&dev_graphics);
314 misc_register (&dev_opengl);
317 void __init gfx_init (const char **name)
320 struct console_ops *console;
321 struct graphics_ops *g;
324 printk ("GFX INIT: ");
331 if ((g = newport_probe (boards, name)) != 0) {
333 graphics_ops_post_init (boards);
337 /* Add more graphic drivers here */
338 /* Keep passing console around */
341 if (boards > MAXCARDS)
342 printk (KERN_WARNING "Too many cards found on the system\n");
346 MODULE_LICENSE("GPL");
348 int init_module(void) {
349 static int initiated = 0;
351 printk("SGI Newport Graphics version %i.%i.%i\n",42,54,69);
356 printk("Adding first board\n");
358 cards[0].g_regs = 0x1f0f0000;
359 cards[0].g_regs_size = sizeof (struct newport_regs);
362 printk("Boards: %d\n", boards);
364 misc_register (&dev_graphics);
365 misc_register (&dev_opengl);
370 void cleanup_module(void) {
371 printk("Shutting down SGI Newport Graphics\n");
373 misc_deregister (&dev_graphics);
374 misc_deregister (&dev_opengl);