Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / drivers / video / via / viafbdev.c
index bdd0e41..289edd5 100644 (file)
@@ -56,6 +56,32 @@ static int viafb_pan_display(struct fb_var_screeninfo *var,
 
 static struct fb_ops viafb_ops;
 
+/* supported output devices on each IGP
+ * only CX700, VX800, VX855, VX900 were documented
+ * VIA_CRT should be everywhere
+ * VIA_6C can be onle pre-CX700 (probably only on CLE266) as 6C is used for PLL
+ * source selection on CX700 and later
+ * K400 seems to support VIA_96, VIA_DVP1, VIA_LVDS{1,2} as in viamode.c
+ */
+static const u32 supported_odev_map[] = {
+       [UNICHROME_CLE266]      = VIA_CRT | VIA_LDVP0 | VIA_LDVP1,
+       [UNICHROME_K400]        = VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1
+                               | VIA_LVDS2,
+       [UNICHROME_K800]        = VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1
+                               | VIA_LVDS2,
+       [UNICHROME_PM800]       = VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1
+                               | VIA_LVDS2,
+       [UNICHROME_CN700]       = VIA_CRT | VIA_DVP0 | VIA_DVP1 | VIA_LVDS1
+                               | VIA_LVDS2,
+       [UNICHROME_CX700]       = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_CN750]       = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_K8M890]      = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_P4M890]      = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_P4M900]      = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_VX800]       = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_VX855]       = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+       [UNICHROME_VX900]       = VIA_CRT | VIA_DVP1 | VIA_LVDS1 | VIA_LVDS2,
+};
 
 static void viafb_fill_var_color_info(struct fb_var_screeninfo *var, u8 depth)
 {
@@ -332,22 +358,22 @@ static int viafb_blank(int blank_mode, struct fb_info *info)
        case FB_BLANK_UNBLANK:
                /* Screen: On, HSync: On, VSync: On */
                /* control CRT monitor power management */
-               viafb_write_reg_mask(CR36, VIACR, 0x00, BIT4 + BIT5);
+               via_set_state(VIA_CRT, VIA_STATE_ON);
                break;
        case FB_BLANK_HSYNC_SUSPEND:
                /* Screen: Off, HSync: Off, VSync: On */
                /* control CRT monitor power management */
-               viafb_write_reg_mask(CR36, VIACR, 0x10, BIT4 + BIT5);
+               via_set_state(VIA_CRT, VIA_STATE_STANDBY);
                break;
        case FB_BLANK_VSYNC_SUSPEND:
                /* Screen: Off, HSync: On, VSync: Off */
                /* control CRT monitor power management */
-               viafb_write_reg_mask(CR36, VIACR, 0x20, BIT4 + BIT5);
+               via_set_state(VIA_CRT, VIA_STATE_SUSPEND);
                break;
        case FB_BLANK_POWERDOWN:
                /* Screen: Off, HSync: Off, VSync: Off */
                /* control CRT monitor power management */
-               viafb_write_reg_mask(CR36, VIACR, 0x30, BIT4 + BIT5);
+               via_set_state(VIA_CRT, VIA_STATE_OFF);
                break;
        }
 
@@ -457,7 +483,7 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
                if (copy_from_user(&gpu32, argp, sizeof(gpu32)))
                        return -EFAULT;
                if (gpu32 & CRT_Device)
-                       viafb_crt_enable();
+                       via_set_state(VIA_CRT, VIA_STATE_ON);
                if (gpu32 & DVI_Device)
                        viafb_dvi_enable();
                if (gpu32 & LCD_Device)
@@ -467,7 +493,7 @@ static int viafb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
                if (copy_from_user(&gpu32, argp, sizeof(gpu32)))
                        return -EFAULT;
                if (gpu32 & CRT_Device)
-                       viafb_crt_disable();
+                       via_set_state(VIA_CRT, VIA_STATE_OFF);
                if (gpu32 & DVI_Device)
                        viafb_dvi_disable();
                if (gpu32 & LCD_Device)
@@ -787,7 +813,8 @@ static int viafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
                bg_color = cursor->image.bg_color;
                if (chip_name == UNICHROME_CX700 ||
                        chip_name == UNICHROME_VX800 ||
-                       chip_name == UNICHROME_VX855) {
+                       chip_name == UNICHROME_VX855 ||
+                       chip_name == UNICHROME_VX900) {
                        fg_color =
                                ((info->cmap.red[fg_color] & 0xFFC0) << 14) |
                                ((info->cmap.green[fg_color] & 0xFFC0) << 4) |
@@ -961,7 +988,7 @@ static void retrieve_device_setting(struct viafb_ioctl_setting
        setting_info->lcd_attributes.lcd_mode = viafb_lcd_mode;
 }
 
-static int parse_active_dev(void)
+static int __init parse_active_dev(void)
 {
        viafb_CRT_ON = STATE_OFF;
        viafb_DVI_ON = STATE_OFF;
@@ -1031,7 +1058,7 @@ static int parse_active_dev(void)
        return 0;
 }
 
-static int parse_port(char *opt_str, int *output_interface)
+static int __devinit parse_port(char *opt_str, int *output_interface)
 {
        if (!strncmp(opt_str, "DVP0", 4))
                *output_interface = INTERFACE_DVP0;
@@ -1048,7 +1075,7 @@ static int parse_port(char *opt_str, int *output_interface)
        return 0;
 }
 
-static void parse_lcd_port(void)
+static void __devinit parse_lcd_port(void)
 {
        parse_port(viafb_lcd_port, &viaparinfo->chip_info->lvds_chip_info.
                output_interface);
@@ -1061,7 +1088,7 @@ static void parse_lcd_port(void)
                  output_interface);
 }
 
-static void parse_dvi_port(void)
+static void __devinit parse_dvi_port(void)
 {
        parse_port(viafb_dvi_port, &viaparinfo->chip_info->tmds_chip_info.
                output_interface);
@@ -1431,38 +1458,196 @@ static const struct file_operations viafb_vt1636_proc_fops = {
        .write          = viafb_vt1636_proc_write,
 };
 
-static void viafb_init_proc(struct proc_dir_entry **viafb_entry)
+#endif /* CONFIG_FB_VIA_DIRECT_PROCFS */
+
+static int viafb_sup_odev_proc_show(struct seq_file *m, void *v)
 {
-       *viafb_entry = proc_mkdir("viafb", NULL);
-       if (*viafb_entry) {
-               proc_create("dvp0", 0, *viafb_entry, &viafb_dvp0_proc_fops);
-               proc_create("dvp1", 0, *viafb_entry, &viafb_dvp1_proc_fops);
-               proc_create("dfph", 0, *viafb_entry, &viafb_dfph_proc_fops);
-               proc_create("dfpl", 0, *viafb_entry, &viafb_dfpl_proc_fops);
-               if (VT1636_LVDS == viaparinfo->chip_info->lvds_chip_info.
-                       lvds_chip_name || VT1636_LVDS ==
-                   viaparinfo->chip_info->lvds_chip_info2.lvds_chip_name) {
-                       proc_create("vt1636", 0, *viafb_entry, &viafb_vt1636_proc_fops);
-               }
+       via_odev_to_seq(m, supported_odev_map[
+               viaparinfo->shared->chip_info.gfx_chip_name]);
+       return 0;
+}
+
+static int viafb_sup_odev_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, viafb_sup_odev_proc_show, NULL);
+}
+
+static const struct file_operations viafb_sup_odev_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = viafb_sup_odev_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static ssize_t odev_update(const char __user *buffer, size_t count, u32 *odev)
+{
+       char buf[64], *ptr = buf;
+       u32 devices;
+       bool add, sub;
 
+       if (count < 1 || count > 63)
+               return -EINVAL;
+       if (copy_from_user(&buf[0], buffer, count))
+               return -EFAULT;
+       buf[count] = '\0';
+       add = buf[0] == '+';
+       sub = buf[0] == '-';
+       if (add || sub)
+               ptr++;
+       devices = via_parse_odev(ptr, &ptr);
+       if (*ptr == '\n')
+               ptr++;
+       if (*ptr != 0)
+               return -EINVAL;
+       if (add)
+               *odev |= devices;
+       else if (sub)
+               *odev &= ~devices;
+       else
+               *odev = devices;
+       return count;
+}
+
+static int viafb_iga1_odev_proc_show(struct seq_file *m, void *v)
+{
+       via_odev_to_seq(m, viaparinfo->shared->iga1_devices);
+       return 0;
+}
+
+static int viafb_iga1_odev_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, viafb_iga1_odev_proc_show, NULL);
+}
+
+static ssize_t viafb_iga1_odev_proc_write(struct file *file,
+       const char __user *buffer, size_t count, loff_t *pos)
+{
+       u32 dev_on, dev_off, dev_old, dev_new;
+       ssize_t res;
+
+       dev_old = dev_new = viaparinfo->shared->iga1_devices;
+       res = odev_update(buffer, count, &dev_new);
+       if (res != count)
+               return res;
+       dev_off = dev_old & ~dev_new;
+       dev_on = dev_new & ~dev_old;
+       viaparinfo->shared->iga1_devices = dev_new;
+       viaparinfo->shared->iga2_devices &= ~dev_new;
+       via_set_state(dev_off, VIA_STATE_OFF);
+       via_set_source(dev_new, IGA1);
+       via_set_state(dev_on, VIA_STATE_ON);
+       return res;
+}
+
+static const struct file_operations viafb_iga1_odev_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = viafb_iga1_odev_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = viafb_iga1_odev_proc_write,
+};
+
+static int viafb_iga2_odev_proc_show(struct seq_file *m, void *v)
+{
+       via_odev_to_seq(m, viaparinfo->shared->iga2_devices);
+       return 0;
+}
+
+static int viafb_iga2_odev_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, viafb_iga2_odev_proc_show, NULL);
+}
+
+static ssize_t viafb_iga2_odev_proc_write(struct file *file,
+       const char __user *buffer, size_t count, loff_t *pos)
+{
+       u32 dev_on, dev_off, dev_old, dev_new;
+       ssize_t res;
+
+       dev_old = dev_new = viaparinfo->shared->iga2_devices;
+       res = odev_update(buffer, count, &dev_new);
+       if (res != count)
+               return res;
+       dev_off = dev_old & ~dev_new;
+       dev_on = dev_new & ~dev_old;
+       viaparinfo->shared->iga2_devices = dev_new;
+       viaparinfo->shared->iga1_devices &= ~dev_new;
+       via_set_state(dev_off, VIA_STATE_OFF);
+       via_set_source(dev_new, IGA2);
+       via_set_state(dev_on, VIA_STATE_ON);
+       return res;
+}
+
+static const struct file_operations viafb_iga2_odev_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = viafb_iga2_odev_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = viafb_iga2_odev_proc_write,
+};
+
+#define IS_VT1636(lvds_chip)   ((lvds_chip).lvds_chip_name == VT1636_LVDS)
+static void viafb_init_proc(struct viafb_shared *shared)
+{
+       struct proc_dir_entry *iga1_entry, *iga2_entry,
+               *viafb_entry = proc_mkdir("viafb", NULL);
+
+       shared->proc_entry = viafb_entry;
+       if (viafb_entry) {
+#ifdef CONFIG_FB_VIA_DIRECT_PROCFS
+               proc_create("dvp0", 0, viafb_entry, &viafb_dvp0_proc_fops);
+               proc_create("dvp1", 0, viafb_entry, &viafb_dvp1_proc_fops);
+               proc_create("dfph", 0, viafb_entry, &viafb_dfph_proc_fops);
+               proc_create("dfpl", 0, viafb_entry, &viafb_dfpl_proc_fops);
+               if (IS_VT1636(shared->chip_info.lvds_chip_info)
+                       || IS_VT1636(shared->chip_info.lvds_chip_info2))
+                       proc_create("vt1636", 0, viafb_entry,
+                               &viafb_vt1636_proc_fops);
+#endif /* CONFIG_FB_VIA_DIRECT_PROCFS */
+
+               proc_create("supported_output_devices", 0, viafb_entry,
+                       &viafb_sup_odev_proc_fops);
+               iga1_entry = proc_mkdir("iga1", viafb_entry);
+               shared->iga1_proc_entry = iga1_entry;
+               proc_create("output_devices", 0, iga1_entry,
+                       &viafb_iga1_odev_proc_fops);
+               iga2_entry = proc_mkdir("iga2", viafb_entry);
+               shared->iga2_proc_entry = iga2_entry;
+               proc_create("output_devices", 0, iga2_entry,
+                       &viafb_iga2_odev_proc_fops);
        }
 }
-static void viafb_remove_proc(struct proc_dir_entry *viafb_entry)
+static void viafb_remove_proc(struct viafb_shared *shared)
 {
-       struct chip_information *chip_info = &viaparinfo->shared->chip_info;
+       struct proc_dir_entry *viafb_entry = shared->proc_entry,
+               *iga1_entry = shared->iga1_proc_entry,
+               *iga2_entry = shared->iga2_proc_entry;
 
+       if (!viafb_entry)
+               return;
+
+       remove_proc_entry("output_devices", iga2_entry);
+       remove_proc_entry("iga2", viafb_entry);
+       remove_proc_entry("output_devices", iga1_entry);
+       remove_proc_entry("iga1", viafb_entry);
+       remove_proc_entry("supported_output_devices", viafb_entry);
+
+#ifdef CONFIG_FB_VIA_DIRECT_PROCFS
        remove_proc_entry("dvp0", viafb_entry);/* parent dir */
        remove_proc_entry("dvp1", viafb_entry);
        remove_proc_entry("dfph", viafb_entry);
        remove_proc_entry("dfpl", viafb_entry);
-       if (chip_info->lvds_chip_info.lvds_chip_name == VT1636_LVDS
-               || chip_info->lvds_chip_info2.lvds_chip_name == VT1636_LVDS)
+       if (IS_VT1636(shared->chip_info.lvds_chip_info)
+               || IS_VT1636(shared->chip_info.lvds_chip_info2))
                remove_proc_entry("vt1636", viafb_entry);
+#endif /* CONFIG_FB_VIA_DIRECT_PROCFS */
 
        remove_proc_entry("viafb", NULL);
 }
-
-#endif /* CONFIG_FB_VIA_DIRECT_PROCFS */
+#undef IS_VT1636
 
 static int parse_mode(const char *str, u32 *xres, u32 *yres)
 {
@@ -1486,6 +1671,39 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres)
 }
 
 
+#ifdef CONFIG_PM
+static int viafb_suspend(void *unused)
+{
+       acquire_console_sem();
+       fb_set_suspend(viafbinfo, 1);
+       viafb_sync(viafbinfo);
+       release_console_sem();
+
+       return 0;
+}
+
+static int viafb_resume(void *unused)
+{
+       acquire_console_sem();
+       if (viaparinfo->shared->vdev->engine_mmio)
+               viafb_reset_engine(viaparinfo);
+       viafb_set_par(viafbinfo);
+       if (viafb_dual_fb)
+               viafb_set_par(viafbinfo1);
+       fb_set_suspend(viafbinfo, 0);
+
+       release_console_sem();
+       return 0;
+}
+
+static struct viafb_pm_hooks viafb_fb_pm_hooks = {
+       .suspend = viafb_suspend,
+       .resume = viafb_resume
+};
+
+#endif
+
+
 int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
 {
        u32 default_xres, default_yres;
@@ -1544,7 +1762,7 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
        viafbinfo->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
 
        viafbinfo->pseudo_palette = pseudo_pal;
-       if (viafb_accel && !viafb_init_engine(viafbinfo)) {
+       if (viafb_accel && !viafb_setup_engine(viafbinfo)) {
                viafbinfo->flags |= FBINFO_HWACCEL_COPYAREA |
                        FBINFO_HWACCEL_FILLRECT |  FBINFO_HWACCEL_IMAGEBLIT;
                default_var.accel_flags = FB_ACCELF_TEXT;
@@ -1671,10 +1889,12 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
                  viafbinfo->node, viafbinfo->fix.id, default_var.xres,
                  default_var.yres, default_var.bits_per_pixel);
 
-#ifdef CONFIG_FB_VIA_DIRECT_PROCFS
-       viafb_init_proc(&viaparinfo->shared->proc_entry);
-#endif
+       viafb_init_proc(viaparinfo->shared);
        viafb_init_dac(IGA2);
+
+#ifdef CONFIG_PM
+       viafb_pm_register(&viafb_fb_pm_hooks);
+#endif
        return 0;
 
 out_fb_unreg:
@@ -1700,9 +1920,7 @@ void __devexit via_fb_pci_remove(struct pci_dev *pdev)
        unregister_framebuffer(viafbinfo);
        if (viafb_dual_fb)
                unregister_framebuffer(viafbinfo1);
-#ifdef CONFIG_FB_VIA_DIRECT_PROCFS
-       viafb_remove_proc(viaparinfo->shared->proc_entry);
-#endif
+       viafb_remove_proc(viaparinfo->shared);
        framebuffer_release(viafbinfo);
        if (viafb_dual_fb)
                framebuffer_release(viafbinfo1);