intelfb: add vsync interrupt support
[safe/jmp/linux-2.6] / drivers / video / fbmem.c
index 6240aed..372aa17 100644 (file)
@@ -55,7 +55,7 @@
 
 #define FBPIXMAPSIZE   (1024 * 8)
 
-static struct notifier_block *fb_notifier_list;
+static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
 struct fb_info *registered_fb[FB_MAX];
 int num_registered_fb;
 
@@ -435,6 +435,11 @@ int fb_prepare_logo(struct fb_info *info, int rotate)
                        depth = info->var.green.length;
        }
 
+       if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) {
+               /* assume console colormap */
+               depth = 4;
+       }
+
        if (depth >= 8) {
                switch (info->fix.visual) {
                case FB_VISUAL_TRUECOLOR:
@@ -589,17 +594,19 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                return info->fbops->fb_read(file, buf, count, ppos);
        
        total_size = info->screen_size;
+
        if (total_size == 0)
                total_size = info->fix.smem_len;
 
        if (p >= total_size)
-           return 0;
+               return 0;
+
        if (count >= total_size)
-           count = total_size;
+               count = total_size;
+
        if (count + p > total_size)
                count = total_size - p;
 
-       cnt = 0;
        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
                         GFP_KERNEL);
        if (!buffer)
@@ -636,6 +643,7 @@ fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        }
 
        kfree(buffer);
+
        return (err) ? err : cnt;
 }
 
@@ -648,7 +656,7 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
        struct fb_info *info = registered_fb[fbidx];
        u32 *buffer, *src;
        u32 __iomem *dst;
-       int c, i, cnt = 0, err;
+       int c, i, cnt = 0, err = 0;
        unsigned long total_size;
 
        if (!info || !info->screen_base)
@@ -661,19 +669,25 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
                return info->fbops->fb_write(file, buf, count, ppos);
        
        total_size = info->screen_size;
+
        if (total_size == 0)
                total_size = info->fix.smem_len;
 
        if (p > total_size)
-           return -ENOSPC;
-       if (count >= total_size)
-           count = total_size;
-       err = 0;
+               return -EFBIG;
+
+       if (count > total_size) {
+               err = -EFBIG;
+               count = total_size;
+       }
+
        if (count + p > total_size) {
-           count = total_size - p;
-           err = -ENOSPC;
+               if (!err)
+                       err = -ENOSPC;
+
+               count = total_size - p;
        }
-       cnt = 0;
+
        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
                         GFP_KERNEL);
        if (!buffer)
@@ -687,12 +701,15 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
        while (count) {
                c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
                src = buffer;
+
                if (copy_from_user(src, buf, c)) {
                        err = -EFAULT;
                        break;
                }
+
                for (i = c >> 2; i--; )
                        fb_writel(*src++, dst++);
+
                if (c & 3) {
                        u8 *src8 = (u8 *) src;
                        u8 __iomem *dst8 = (u8 __iomem *) dst;
@@ -702,14 +719,16 @@ fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 
                        dst = (u32 __iomem *) dst8;
                }
+
                *ppos += c;
                buf += c;
                cnt += c;
                count -= c;
        }
+
        kfree(buffer);
 
-       return (err) ? err : cnt;
+       return (cnt) ? cnt : err;
 }
 
 #ifdef CONFIG_KMOD
@@ -722,14 +741,30 @@ static void try_to_load(int fb)
 int
 fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
 {
+       struct fb_fix_screeninfo *fix = &info->fix;
         int xoffset = var->xoffset;
         int yoffset = var->yoffset;
-        int err;
+        int err = 0, yres = info->var.yres;
+
+       if (var->yoffset > 0) {
+               if (var->vmode & FB_VMODE_YWRAP) {
+                       if (!fix->ywrapstep || (var->yoffset % fix->ywrapstep))
+                               err = -EINVAL;
+                       else
+                               yres = 0;
+               } else if (!fix->ypanstep || (var->yoffset % fix->ypanstep))
+                       err = -EINVAL;
+       }
+
+       if (var->xoffset > 0 && (!fix->xpanstep ||
+                                (var->xoffset % fix->xpanstep)))
+               err = -EINVAL;
+
+        if (err || !info->fbops->fb_pan_display || xoffset < 0 ||
+           yoffset < 0 || var->yoffset + yres > info->var.yres_virtual ||
+           var->xoffset + info->var.xres > info->var.xres_virtual)
+               return -EINVAL;
 
-        if (xoffset < 0 || yoffset < 0 || !info->fbops->fb_pan_display ||
-            xoffset + info->var.xres > info->var.xres_virtual ||
-            yoffset + info->var.yres > info->var.yres_virtual)
-                return -EINVAL;
        if ((err = info->fbops->fb_pan_display(var, info)))
                return err;
         info->var.xoffset = var->xoffset;
@@ -760,7 +795,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 
                    event.info = info;
                    event.data = &mode1;
-                   ret = notifier_call_chain(&fb_notifier_list,
+                   ret = blocking_notifier_call_chain(&fb_notifier_list,
                                              FB_EVENT_MODE_DELETE, &event);
                }
 
@@ -806,8 +841,8 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
 
                                info->flags &= ~FBINFO_MISC_USEREVENT;
                                event.info = info;
-                               notifier_call_chain(&fb_notifier_list, evnt,
-                                                   &event);
+                               blocking_notifier_call_chain(&fb_notifier_list,
+                                               evnt, &event);
                        }
                }
        }
@@ -830,7 +865,8 @@ fb_blank(struct fb_info *info, int blank)
 
                event.info = info;
                event.data = &blank;
-               notifier_call_chain(&fb_notifier_list, FB_EVENT_BLANK, &event);
+               blocking_notifier_call_chain(&fb_notifier_list,
+                               FB_EVENT_BLANK, &event);
        }
 
        return ret;
@@ -901,7 +937,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                con2fb.framebuffer = -1;
                event.info = info;
                event.data = &con2fb;
-               notifier_call_chain(&fb_notifier_list,
+               blocking_notifier_call_chain(&fb_notifier_list,
                                    FB_EVENT_GET_CONSOLE_MAP, &event);
                return copy_to_user(argp, &con2fb,
                                    sizeof(con2fb)) ? -EFAULT : 0;
@@ -920,7 +956,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
                    return -EINVAL;
                event.info = info;
                event.data = &con2fb;
-               return notifier_call_chain(&fb_notifier_list,
+               return blocking_notifier_call_chain(&fb_notifier_list,
                                           FB_EVENT_SET_CONSOLE_MAP,
                                           &event);
        case FBIOBLANK:
@@ -933,7 +969,7 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
        default:
                if (fb->fb_ioctl == NULL)
                        return -EINVAL;
-               return fb->fb_ioctl(inode, file, cmd, arg, info);
+               return fb->fb_ioctl(info, cmd, arg);
        }
 }
 
@@ -1083,7 +1119,7 @@ fb_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        default:
                if (fb->fb_compat_ioctl)
-                       ret = fb->fb_compat_ioctl(file, cmd, arg, info);
+                       ret = fb->fb_compat_ioctl(info, cmd, arg);
                break;
        }
        unlock_kernel();
@@ -1111,7 +1147,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        if (fb->fb_mmap) {
                int res;
                lock_kernel();
-               res = fb->fb_mmap(info, file, vma);
+               res = fb->fb_mmap(info, vma);
                unlock_kernel();
                return res;
        }
@@ -1145,11 +1181,6 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        vma->vm_pgoff = off >> PAGE_SHIFT;
        /* This is an IO map - tell maydump to skip this VMA */
        vma->vm_flags |= VM_IO | VM_RESERVED;
-#if defined(__sparc_v9__)
-       if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
-                               vma->vm_end - vma->vm_start, vma->vm_page_prot))
-               return -EAGAIN;
-#else
 #if defined(__mc68000__)
 #if defined(CONFIG_SUN3)
        pgprot_val(vma->vm_page_prot) |= SUN3_PAGE_NOCACHE;
@@ -1171,7 +1202,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
 #elif defined(__i386__) || defined(__x86_64__)
        if (boot_cpu_data.x86 > 3)
                pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
-#elif defined(__mips__)
+#elif defined(__mips__) || defined(__sparc_v9__)
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 #elif defined(__hppa__)
        pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE;
@@ -1188,7 +1219,6 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
                             vma->vm_end - vma->vm_start, vma->vm_page_prot))
                return -EAGAIN;
-#endif /* !__sparc_v9__ */
        return 0;
 #endif /* !sparc32 */
 }
@@ -1210,6 +1240,7 @@ fb_open(struct inode *inode, struct file *file)
                return -ENODEV;
        if (!try_module_get(info->fbops->owner))
                return -ENODEV;
+       file->private_data = info;
        if (info->fbops->fb_open) {
                res = info->fbops->fb_open(info,1);
                if (res)
@@ -1221,11 +1252,9 @@ fb_open(struct inode *inode, struct file *file)
 static int 
 fb_release(struct inode *inode, struct file *file)
 {
-       int fbidx = iminor(inode);
-       struct fb_info *info;
+       struct fb_info * const info = file->private_data;
 
        lock_kernel();
-       info = registered_fb[fbidx];
        if (info->fbops->fb_release)
                info->fbops->fb_release(info,1);
        module_put(info->fbops->owner);
@@ -1307,7 +1336,7 @@ register_framebuffer(struct fb_info *fb_info)
        devfs_mk_cdev(MKDEV(FB_MAJOR, i),
                        S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i);
        event.info = fb_info;
-       notifier_call_chain(&fb_notifier_list,
+       blocking_notifier_call_chain(&fb_notifier_list,
                            FB_EVENT_FB_REGISTERED, &event);
        return 0;
 }
@@ -1349,7 +1378,7 @@ unregister_framebuffer(struct fb_info *fb_info)
  */
 int fb_register_client(struct notifier_block *nb)
 {
-       return notifier_chain_register(&fb_notifier_list, nb);
+       return blocking_notifier_chain_register(&fb_notifier_list, nb);
 }
 
 /**
@@ -1358,7 +1387,7 @@ int fb_register_client(struct notifier_block *nb)
  */
 int fb_unregister_client(struct notifier_block *nb)
 {
-       return notifier_chain_unregister(&fb_notifier_list, nb);
+       return blocking_notifier_chain_unregister(&fb_notifier_list, nb);
 }
 
 /**
@@ -1376,11 +1405,13 @@ void fb_set_suspend(struct fb_info *info, int state)
 
        event.info = info;
        if (state) {
-               notifier_call_chain(&fb_notifier_list, FB_EVENT_SUSPEND, &event);
+               blocking_notifier_call_chain(&fb_notifier_list,
+                               FB_EVENT_SUSPEND, &event);
                info->state = FBINFO_STATE_SUSPENDED;
        } else {
                info->state = FBINFO_STATE_RUNNING;
-               notifier_call_chain(&fb_notifier_list, FB_EVENT_RESUME, &event);
+               blocking_notifier_call_chain(&fb_notifier_list,
+                               FB_EVENT_RESUME, &event);
        }
 }
 
@@ -1452,7 +1483,7 @@ int fb_new_modelist(struct fb_info *info)
 
        if (!list_empty(&info->modelist)) {
                event.info = info;
-               err = notifier_call_chain(&fb_notifier_list,
+               err = blocking_notifier_call_chain(&fb_notifier_list,
                                           FB_EVENT_NEW_MODELIST,
                                           &event);
        }
@@ -1478,7 +1509,7 @@ int fb_con_duit(struct fb_info *info, int event, void *data)
        evnt.info = info;
        evnt.data = data;
 
-       return notifier_call_chain(&fb_notifier_list, event, &evnt);
+       return blocking_notifier_call_chain(&fb_notifier_list, event, &evnt);
 }
 EXPORT_SYMBOL(fb_con_duit);
 
@@ -1527,6 +1558,7 @@ int fb_get_options(char *name, char **option)
        return retval;
 }
 
+#ifndef MODULE
 /**
  *     video_setup - process command line options
  *     @options: string of options
@@ -1567,9 +1599,10 @@ static int __init video_setup(char *options)
                }
        }
 
-       return 0;
+       return 1;
 }
 __setup("video=", video_setup);
+#endif
 
     /*
      *  Visible symbols for modules