V4L/DVB: media/IR/imon: precendence issue: ! vs ==
[safe/jmp/linux-2.6] / drivers / video / au1100fb.c
index 3d04b2d..40f6132 100644 (file)
@@ -7,6 +7,9 @@
  *     Karl Lessard <klessard@sunrisetelecom.com>
  *     <c.pellegrin@exadron.com>
  *
+ * PM support added by Rodolfo Giometti <giometti@linux.it>
+ * Cursor enable/disable by Rodolfo Giometti <giometti@linux.it>
+ *
  * Copyright 2002 MontaVista Software
  * Author: MontaVista Software, Inc.
  *             ppopov@mvista.com or source@mvista.com
@@ -38,7 +41,6 @@
  *  with this program; if not, write  to the Free Software Foundation, Inc.,
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -50,6 +52,7 @@
 #include <linux/ctype.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include <asm/mach-au1x00/au1000.h>
 
@@ -109,6 +112,56 @@ static struct fb_var_screeninfo au1100fb_var __initdata = {
 
 static struct au1100fb_drv_info drv_info;
 
+static int nocursor = 0;
+module_param(nocursor, int, 0644);
+MODULE_PARM_DESC(nocursor, "cursor enable/disable");
+
+/* fb_blank
+ * Blank the screen. Depending on the mode, the screen will be
+ * activated with the backlight color, or desactivated
+ */
+static int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
+{
+       struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
+
+       print_dbg("fb_blank %d %p", blank_mode, fbi);
+
+       switch (blank_mode) {
+
+       case VESA_NO_BLANKING:
+                       /* Turn on panel */
+                       fbdev->regs->lcd_control |= LCD_CONTROL_GO;
+#ifdef CONFIG_MIPS_PB1100
+                       if (drv_info.panel_idx == 1) {
+                               au_writew(au_readw(PB1100_G_CONTROL)
+                                         | (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
+                       PB1100_G_CONTROL);
+                       }
+#endif
+               au_sync();
+               break;
+
+       case VESA_VSYNC_SUSPEND:
+       case VESA_HSYNC_SUSPEND:
+       case VESA_POWERDOWN:
+                       /* Turn off panel */
+                       fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
+#ifdef CONFIG_MIPS_PB1100
+                       if (drv_info.panel_idx == 1) {
+                               au_writew(au_readw(PB1100_G_CONTROL)
+                                         & ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
+                       PB1100_G_CONTROL);
+                       }
+#endif
+               au_sync();
+               break;
+       default:
+               break;
+
+       }
+       return 0;
+}
+
 /*
  * Set hardware with var settings. This will enable the controller with a specific
  * mode, normally validated with the fb_check_var method
@@ -155,7 +208,7 @@ int au1100fb_setmode(struct au1100fb_device *fbdev)
 
                        info->fix.visual = FB_VISUAL_TRUECOLOR;
                        info->fix.line_length = info->var.xres_virtual << 1; /* depth=16 */
-       }
+               }
        } else {
                /* mono */
                info->fix.visual = FB_VISUAL_MONO10;
@@ -163,20 +216,16 @@ int au1100fb_setmode(struct au1100fb_device *fbdev)
        }
 
        info->screen_size = info->fix.line_length * info->var.yres_virtual;
+       info->var.rotate = ((fbdev->panel->control_base&LCD_CONTROL_SM_MASK) \
+                               >> LCD_CONTROL_SM_BIT) * 90;
 
        /* Determine BPP mode and format */
-       fbdev->regs->lcd_control = fbdev->panel->control_base |
-                           ((info->var.rotate/90) << LCD_CONTROL_SM_BIT);
-
-       fbdev->regs->lcd_intenable = 0;
-       fbdev->regs->lcd_intstatus = 0;
-
+       fbdev->regs->lcd_control = fbdev->panel->control_base;
        fbdev->regs->lcd_horztiming = fbdev->panel->horztiming;
-
        fbdev->regs->lcd_verttiming = fbdev->panel->verttiming;
-
        fbdev->regs->lcd_clkcontrol = fbdev->panel->clkcontrol_base;
-
+       fbdev->regs->lcd_intenable = 0;
+       fbdev->regs->lcd_intstatus = 0;
        fbdev->regs->lcd_dmaaddr0 = LCD_DMA_SA_N(fbdev->fb_phys);
 
        if (panel_is_dual(fbdev->panel)) {
@@ -205,6 +254,8 @@ int au1100fb_setmode(struct au1100fb_device *fbdev)
 
        /* Resume controller */
        fbdev->regs->lcd_control |= LCD_CONTROL_GO;
+       mdelay(10);
+       au1100fb_fb_blank(VESA_NO_BLANKING, info);
 
        return 0;
 }
@@ -214,10 +265,13 @@ int au1100fb_setmode(struct au1100fb_device *fbdev)
  */
 int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *fbi)
 {
-       struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
-       u32 *palette = fbdev->regs->lcd_pallettebase;
+       struct au1100fb_device *fbdev;
+       u32 *palette;
        u32 value;
 
+       fbdev = to_au1100fb_device(fbi);
+       palette = fbdev->regs->lcd_pallettebase;
+
        if (regno > (AU1100_LCD_NBR_PALETTE_ENTRIES - 1))
                return -EINVAL;
 
@@ -265,60 +319,16 @@ int au1100fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned
        return 0;
 }
 
-/* fb_blank
- * Blank the screen. Depending on the mode, the screen will be
- * activated with the backlight color, or desactivated
- */
-int au1100fb_fb_blank(int blank_mode, struct fb_info *fbi)
-{
-       struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
-
-       print_dbg("fb_blank %d %p", blank_mode, fbi);
-
-       switch (blank_mode) {
-
-       case VESA_NO_BLANKING:
-                       /* Turn on panel */
-                       fbdev->regs->lcd_control |= LCD_CONTROL_GO;
-#ifdef CONFIG_MIPS_PB1100
-                       if (drv_info.panel_idx == 1) {
-                               au_writew(au_readw(PB1100_G_CONTROL)
-                                         | (PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
-                       PB1100_G_CONTROL);
-                       }
-#endif
-               au_sync();
-               break;
-
-       case VESA_VSYNC_SUSPEND:
-       case VESA_HSYNC_SUSPEND:
-       case VESA_POWERDOWN:
-                       /* Turn off panel */
-                       fbdev->regs->lcd_control &= ~LCD_CONTROL_GO;
-#ifdef CONFIG_MIPS_PB1100
-                       if (drv_info.panel_idx == 1) {
-                               au_writew(au_readw(PB1100_G_CONTROL)
-                                         & ~(PB1100_G_CONTROL_BL | PB1100_G_CONTROL_VDD),
-                       PB1100_G_CONTROL);
-                       }
-#endif
-               au_sync();
-               break;
-       default:
-               break;
-
-       }
-       return 0;
-}
-
 /* fb_pan_display
  * Pan display in x and/or y as specified
  */
 int au1100fb_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi)
 {
-       struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
+       struct au1100fb_device *fbdev;
        int dy;
 
+       fbdev = to_au1100fb_device(fbi);
+
        print_dbg("fb_pan_display %p %p", var, fbi);
 
        if (!var || !fbdev) {
@@ -382,10 +392,12 @@ void au1100fb_fb_rotate(struct fb_info *fbi, int angle)
  */
 int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
 {
-       struct au1100fb_device *fbdev = to_au1100fb_device(fbi);
+       struct au1100fb_device *fbdev;
        unsigned int len;
        unsigned long start=0, off;
 
+       fbdev = to_au1100fb_device(fbi);
+
        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
                return -EINVAL;
        }
@@ -416,6 +428,17 @@ int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
        return 0;
 }
 
+/* fb_cursor
+ * Used to disable cursor drawing...
+ */
+int au1100fb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+       if (nocursor)
+               return 0;
+       else
+               return -EINVAL; /* just to force soft_cursor() call */
+}
+
 static struct fb_ops au1100fb_ops =
 {
        .owner                  = THIS_MODULE,
@@ -427,6 +450,7 @@ static struct fb_ops au1100fb_ops =
        .fb_imageblit           = cfb_imageblit,
        .fb_rotate              = au1100fb_fb_rotate,
        .fb_mmap                = au1100fb_fb_mmap,
+       .fb_cursor              = au1100fb_fb_cursor,
 };
 
 
@@ -434,7 +458,7 @@ static struct fb_ops au1100fb_ops =
 
 /* AU1100 LCD controller device driver */
 
-int au1100fb_drv_probe(struct device *dev)
+static int __init au1100fb_drv_probe(struct platform_device *dev)
 {
        struct au1100fb_device *fbdev = NULL;
        struct resource *regs_res;
@@ -445,15 +469,14 @@ int au1100fb_drv_probe(struct device *dev)
                        return -EINVAL;
 
        /* Allocate new device private */
-       if (!(fbdev = kmalloc(sizeof(struct au1100fb_device), GFP_KERNEL))) {
+       if (!(fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL))) {
                print_err("fail to allocate device private record");
                return -ENOMEM;
        }
-       memset((void*)fbdev, 0, sizeof(struct au1100fb_device));
 
        fbdev->panel = &known_lcd_panels[drv_info.panel_idx];
 
-       dev_set_drvdata(dev, (void*)fbdev);
+       platform_set_drvdata(dev, (void *)fbdev);
 
        /* Allocate region for our registers and map them */
        if (!(regs_res = platform_get_resource(to_platform_device(dev),
@@ -467,7 +490,7 @@ int au1100fb_drv_probe(struct device *dev)
 
        if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len,
                                DRIVER_NAME)) {
-               print_err("fail to lock memory region at 0x%08x",
+               print_err("fail to lock memory region at 0x%08lx",
                                au1100fb_fix.mmio_start);
                return -EBUSY;
        }
@@ -526,10 +549,9 @@ int au1100fb_drv_probe(struct device *dev)
        fbdev->info.fbops = &au1100fb_ops;
        fbdev->info.fix = au1100fb_fix;
 
-       if (!(fbdev->info.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL))) {
+       if (!(fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL))) {
                return -ENOMEM;
        }
-       memset(fbdev->info.pseudo_palette, 0, sizeof(u32) * 16);
 
        if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
                print_err("Fail to allocate colormap (%d entries)",
@@ -562,19 +584,19 @@ failed:
                fb_dealloc_cmap(&fbdev->info.cmap);
        }
        kfree(fbdev);
-       dev_set_drvdata(dev, NULL);
+       platform_set_drvdata(dev, NULL);
 
        return 0;
 }
 
-int au1100fb_drv_remove(struct device *dev)
+int au1100fb_drv_remove(struct platform_device *dev)
 {
        struct au1100fb_device *fbdev = NULL;
 
        if (!dev)
                return -ENODEV;
 
-       fbdev = (struct au1100fb_device*) dev_get_drvdata(dev);
+       fbdev = (struct au1100fb_device *) platform_get_drvdata(dev);
 
 #if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
        au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);
@@ -595,22 +617,58 @@ int au1100fb_drv_remove(struct device *dev)
        return 0;
 }
 
-int au1100fb_drv_suspend(struct device *dev, u32 state, u32 level)
+#ifdef CONFIG_PM
+static u32 sys_clksrc;
+static struct au1100fb_regs fbregs;
+
+int au1100fb_drv_suspend(struct platform_device *dev, pm_message_t state)
 {
-       /* TODO */
+       struct au1100fb_device *fbdev = platform_get_drvdata(dev);
+
+       if (!fbdev)
+               return 0;
+
+       /* Save the clock source state */
+       sys_clksrc = au_readl(SYS_CLKSRC);
+
+       /* Blank the LCD */
+       au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);
+
+       /* Stop LCD clocking */
+       au_writel(sys_clksrc & ~SYS_CS_ML_MASK, SYS_CLKSRC);
+
+       memcpy(&fbregs, fbdev->regs, sizeof(struct au1100fb_regs));
+
        return 0;
 }
 
-int au1100fb_drv_resume(struct device *dev, u32 level)
+int au1100fb_drv_resume(struct platform_device *dev)
 {
-       /* TODO */
+       struct au1100fb_device *fbdev = platform_get_drvdata(dev);
+
+       if (!fbdev)
+               return 0;
+
+       memcpy(fbdev->regs, &fbregs, sizeof(struct au1100fb_regs));
+
+       /* Restart LCD clocking */
+       au_writel(sys_clksrc, SYS_CLKSRC);
+
+       /* Unblank the LCD */
+       au1100fb_fb_blank(VESA_NO_BLANKING, &fbdev->info);
+
        return 0;
 }
+#else
+#define au1100fb_drv_suspend NULL
+#define au1100fb_drv_resume NULL
+#endif
 
-static struct device_driver au1100fb_driver = {
-       .name           = "au1100-lcd",
-       .bus            = &platform_bus_type,
-
+static struct platform_driver au1100fb_driver = {
+       .driver = {
+               .name           = "au1100-lcd",
+               .owner          = THIS_MODULE,
+       },
        .probe          = au1100fb_drv_probe,
         .remove                = au1100fb_drv_remove,
        .suspend        = au1100fb_drv_suspend,
@@ -636,7 +694,7 @@ int au1100fb_setup(char *options)
        if (options) {
                while ((this_opt = strsep(&options,",")) != NULL) {
                        /* Panel option */
-               if (!strncmp(this_opt, "panel:", 6)) {
+                       if (!strncmp(this_opt, "panel:", 6)) {
                                int i;
                                this_opt += 6;
                                for (i = 0; i < num_panels; i++) {
@@ -644,22 +702,30 @@ int au1100fb_setup(char *options)
                                                     known_lcd_panels[i].name,
                                                        strlen(this_opt))) {
                                                panel_idx = i;
-                                       break;
+                                               break;
+                                       }
                                }
-                       }
                                if (i >= num_panels) {
                                        print_warn("Panel %s not supported!", this_opt);
                                }
                        }
+                       if (!strncmp(this_opt, "nocursor", 8)) {
+                               this_opt += 8;
+                               nocursor = 1;
+                               print_info("Cursor disabled");
+                       }
                        /* Mode option (only option that start with digit) */
                        else if (isdigit(this_opt[0])) {
-                               mode = kmalloc(strlen(this_opt) + 1, GFP_KERNEL);
-                               strncpy(mode, this_opt, strlen(this_opt) + 1);
+                               mode = kstrdup(this_opt, GFP_KERNEL);
+                               if (!mode) {
+                                       print_err("memory allocation failed");
+                                       return -ENOMEM;
+                               }
                        }
                        /* Unsupported option */
                        else {
                                print_warn("Unsupported option \"%s\"", this_opt);
-               }
+                       }
                }
        }
 
@@ -692,15 +758,14 @@ int __init au1100fb_init(void)
                return ret;
        }
 
-       return driver_register(&au1100fb_driver);
+       return platform_driver_register(&au1100fb_driver);
 }
 
 void __exit au1100fb_cleanup(void)
 {
-       driver_unregister(&au1100fb_driver);
+       platform_driver_unregister(&au1100fb_driver);
 
-       if (drv_info.opt_mode)
-               kfree(drv_info.opt_mode);
+       kfree(drv_info.opt_mode);
 }
 
 module_init(au1100fb_init);