]> Pileus Git - ~andy/linux/blobdiff - drivers/video/s3c-fb.c
Merge branch 'next' of git://git.monstr.eu/linux-2.6-microblaze
[~andy/linux] / drivers / video / s3c-fb.c
index 0753b1cfcb8bc9eb6df5fa234ac2441b9c48470b..0c63b69b6340b5eb271753dda31f02ecd59766a2 100644 (file)
@@ -192,6 +192,7 @@ struct s3c_fb_vsync {
  * @regs: The mapped hardware registers.
  * @variant: Variant information for this hardware.
  * @enabled: A bitmask of enabled hardware windows.
+ * @output_on: Flag if the physical output is enabled.
  * @pdata: The platform configuration data passed with the device.
  * @windows: The hardware windows that have been claimed.
  * @irq_no: IRQ line number
@@ -208,6 +209,7 @@ struct s3c_fb {
        struct s3c_fb_variant    variant;
 
        unsigned char            enabled;
+       bool                     output_on;
 
        struct s3c_fb_platdata  *pdata;
        struct s3c_fb_win       *windows[S3C_FB_MAX_WIN];
@@ -440,6 +442,39 @@ static void shadow_protect_win(struct s3c_fb_win *win, bool protect)
        }
 }
 
+/**
+ * s3c_fb_enable() - Set the state of the main LCD output
+ * @sfb: The main framebuffer state.
+ * @enable: The state to set.
+ */
+static void s3c_fb_enable(struct s3c_fb *sfb, int enable)
+{
+       u32 vidcon0 = readl(sfb->regs + VIDCON0);
+
+       if (enable && !sfb->output_on)
+               pm_runtime_get_sync(sfb->dev);
+
+       if (enable) {
+               vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
+       } else {
+               /* see the note in the framebuffer datasheet about
+                * why you cannot take both of these bits down at the
+                * same time. */
+
+               if (vidcon0 & VIDCON0_ENVID) {
+                       vidcon0 |= VIDCON0_ENVID;
+                       vidcon0 &= ~VIDCON0_ENVID_F;
+               }
+       }
+
+       writel(vidcon0, sfb->regs + VIDCON0);
+
+       if (!enable && sfb->output_on)
+               pm_runtime_put_sync(sfb->dev);
+
+       sfb->output_on = enable;
+}
+
 /**
  * s3c_fb_set_par() - framebuffer request to set new framebuffer state.
  * @info: The framebuffer to change.
@@ -461,6 +496,8 @@ static int s3c_fb_set_par(struct fb_info *info)
 
        dev_dbg(sfb->dev, "setting framebuffer parameters\n");
 
+       pm_runtime_get_sync(sfb->dev);
+
        shadow_protect_win(win, 1);
 
        switch (var->bits_per_pixel) {
@@ -510,9 +547,10 @@ static int s3c_fb_set_par(struct fb_info *info)
                if (sfb->variant.is_2443)
                        data |= (1 << 5);
 
-               data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
                writel(data, regs + VIDCON0);
 
+               s3c_fb_enable(sfb, 1);
+
                data = VIDTCON0_VBPD(var->upper_margin - 1) |
                       VIDTCON0_VFPD(var->lower_margin - 1) |
                       VIDTCON0_VSPW(var->vsync_len - 1);
@@ -574,6 +612,7 @@ static int s3c_fb_set_par(struct fb_info *info)
        }
 
        data = WINCONx_ENWIN;
+       sfb->enabled |= (1 << win->index);
 
        /* note, since we have to round up the bits-per-pixel, we end up
         * relying on the bitfield information for r/g/b/a to work out
@@ -621,7 +660,8 @@ static int s3c_fb_set_par(struct fb_info *info)
                } else if (var->transp.length == 1)
                        data |= WINCON1_BPPMODE_25BPP_A1888
                                | WINCON1_BLD_PIX;
-               else if (var->transp.length == 4)
+               else if ((var->transp.length == 4) ||
+                       (var->transp.length == 8))
                        data |= WINCON1_BPPMODE_28BPP_A4888
                                | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
                else
@@ -654,6 +694,8 @@ static int s3c_fb_set_par(struct fb_info *info)
 
        shadow_protect_win(win, 0);
 
+       pm_runtime_put_sync(sfb->dev);
+
        return 0;
 }
 
@@ -725,6 +767,8 @@ static int s3c_fb_setcolreg(unsigned regno,
        dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n",
                __func__, win->index, regno, red, green, blue);
 
+       pm_runtime_get_sync(sfb->dev);
+
        switch (info->fix.visual) {
        case FB_VISUAL_TRUECOLOR:
                /* true-colour, use pseudo-palette */
@@ -752,38 +796,14 @@ static int s3c_fb_setcolreg(unsigned regno,
                break;
 
        default:
+               pm_runtime_put_sync(sfb->dev);
                return 1;       /* unknown type */
        }
 
+       pm_runtime_put_sync(sfb->dev);
        return 0;
 }
 
-/**
- * s3c_fb_enable() - Set the state of the main LCD output
- * @sfb: The main framebuffer state.
- * @enable: The state to set.
- */
-static void s3c_fb_enable(struct s3c_fb *sfb, int enable)
-{
-       u32 vidcon0 = readl(sfb->regs + VIDCON0);
-
-       if (enable)
-               vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
-       else {
-               /* see the note in the framebuffer datasheet about
-                * why you cannot take both of these bits down at the
-                * same time. */
-
-               if (!(vidcon0 & VIDCON0_ENVID))
-                       return;
-
-               vidcon0 |= VIDCON0_ENVID;
-               vidcon0 &= ~VIDCON0_ENVID_F;
-       }
-
-       writel(vidcon0, sfb->regs + VIDCON0);
-}
-
 /**
  * s3c_fb_blank() - blank or unblank the given window
  * @blank_mode: The blank state from FB_BLANK_*
@@ -800,6 +820,8 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
 
        dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
 
+       pm_runtime_get_sync(sfb->dev);
+
        wincon = readl(sfb->regs + sfb->variant.wincon + (index * 4));
 
        switch (blank_mode) {
@@ -810,12 +832,16 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
 
        case FB_BLANK_NORMAL:
                /* disable the DMA and display 0x0 (black) */
+               shadow_protect_win(win, 1);
                writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
                       sfb->regs + sfb->variant.winmap + (index * 4));
+               shadow_protect_win(win, 0);
                break;
 
        case FB_BLANK_UNBLANK:
+               shadow_protect_win(win, 1);
                writel(0x0, sfb->regs + sfb->variant.winmap + (index * 4));
+               shadow_protect_win(win, 0);
                wincon |= WINCONx_ENWIN;
                sfb->enabled |= (1 << index);
                break;
@@ -823,10 +849,13 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
        case FB_BLANK_VSYNC_SUSPEND:
        case FB_BLANK_HSYNC_SUSPEND:
        default:
+               pm_runtime_put_sync(sfb->dev);
                return 1;
        }
 
+       shadow_protect_win(win, 1);
        writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4));
+       shadow_protect_win(win, 0);
 
        /* Check the enabled state to see if we need to be running the
         * main LCD interface, as if there are no active windows then
@@ -845,8 +874,13 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info)
        /* we're stuck with this until we can do something about overriding
         * the power control using the blanking event for a single fb.
         */
-       if (index == sfb->pdata->default_win)
+       if (index == sfb->pdata->default_win) {
+               shadow_protect_win(win, 1);
                s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
+               shadow_protect_win(win, 0);
+       }
+
+       pm_runtime_put_sync(sfb->dev);
 
        return 0;
 }
@@ -870,6 +904,8 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
        void __iomem *buf       = sfb->regs + win->index * 8;
        unsigned int start_boff, end_boff;
 
+       pm_runtime_get_sync(sfb->dev);
+
        /* Offset in bytes to the start of the displayed area */
        start_boff = var->yoffset * info->fix.line_length;
        /* X offset depends on the current bpp */
@@ -888,6 +924,7 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
                        break;
                default:
                        dev_err(sfb->dev, "invalid bpp\n");
+                       pm_runtime_put_sync(sfb->dev);
                        return -EINVAL;
                }
        }
@@ -903,6 +940,7 @@ static int s3c_fb_pan_display(struct fb_var_screeninfo *var,
 
        shadow_protect_win(win, 0);
 
+       pm_runtime_put_sync(sfb->dev);
        return 0;
 }
 
@@ -992,11 +1030,16 @@ static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc)
        if (crtc != 0)
                return -ENODEV;
 
+       pm_runtime_get_sync(sfb->dev);
+
        count = sfb->vsync_info.count;
        s3c_fb_enable_irq(sfb);
        ret = wait_event_interruptible_timeout(sfb->vsync_info.wait,
                                       count != sfb->vsync_info.count,
                                       msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));
+
+       pm_runtime_put_sync(sfb->dev);
+
        if (ret == 0)
                return -ETIMEDOUT;
 
@@ -1027,30 +1070,8 @@ static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,
        return ret;
 }
 
-static int s3c_fb_open(struct fb_info *info, int user)
-{
-       struct s3c_fb_win *win = info->par;
-       struct s3c_fb *sfb = win->parent;
-
-       pm_runtime_get_sync(sfb->dev);
-
-       return 0;
-}
-
-static int s3c_fb_release(struct fb_info *info, int user)
-{
-       struct s3c_fb_win *win = info->par;
-       struct s3c_fb *sfb = win->parent;
-
-       pm_runtime_put_sync(sfb->dev);
-
-       return 0;
-}
-
 static struct fb_ops s3c_fb_ops = {
        .owner          = THIS_MODULE,
-       .fb_open        = s3c_fb_open,
-       .fb_release     = s3c_fb_release,
        .fb_check_var   = s3c_fb_check_var,
        .fb_set_par     = s3c_fb_set_par,
        .fb_blank       = s3c_fb_blank,
@@ -1452,7 +1473,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
                        dev_err(dev, "failed to create window %d\n", win);
                        for (; win >= 0; win--)
                                s3c_fb_release_win(sfb, sfb->windows[win]);
-                       goto err_irq;
+                       goto err_pm_runtime;
                }
        }
 
@@ -1461,7 +1482,8 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
 
        return 0;
 
-err_irq:
+err_pm_runtime:
+       pm_runtime_put_sync(sfb->dev);
        free_irq(sfb->irq_no, sfb);
 
 err_ioremap:
@@ -1471,6 +1493,8 @@ err_req_region:
        release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
 
 err_lcd_clk:
+       pm_runtime_disable(sfb->dev);
+
        if (!sfb->variant.has_clksel) {
                clk_disable(sfb->lcd_clk);
                clk_put(sfb->lcd_clk);
@@ -1524,7 +1548,7 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 static int s3c_fb_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -1571,10 +1595,15 @@ static int s3c_fb_resume(struct device *dev)
 
        for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
                void __iomem *regs = sfb->regs + sfb->variant.keycon;
+               win = sfb->windows[win_no];
+               if (!win)
+                       continue;
 
+               shadow_protect_win(win, 1);
                regs += (win_no * 8);
                writel(0xffffff, regs + WKEYCON0);
                writel(0xffffff, regs + WKEYCON1);
+               shadow_protect_win(win, 0);
        }
 
        /* restore framebuffers */
@@ -1589,27 +1618,19 @@ static int s3c_fb_resume(struct device *dev)
 
        return 0;
 }
+#endif
 
+#ifdef CONFIG_PM_RUNTIME
 static int s3c_fb_runtime_suspend(struct device *dev)
 {
        struct platform_device *pdev = to_platform_device(dev);
        struct s3c_fb *sfb = platform_get_drvdata(pdev);
-       struct s3c_fb_win *win;
-       int win_no;
-
-       for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) {
-               win = sfb->windows[win_no];
-               if (!win)
-                       continue;
-
-               /* use the blank function to push into power-down */
-               s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
-       }
 
        if (!sfb->variant.has_clksel)
                clk_disable(sfb->lcd_clk);
 
        clk_disable(sfb->bus_clk);
+
        return 0;
 }
 
@@ -1618,8 +1639,6 @@ static int s3c_fb_runtime_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct s3c_fb *sfb = platform_get_drvdata(pdev);
        struct s3c_fb_platdata *pd = sfb->pdata;
-       struct s3c_fb_win *win;
-       int win_no;
 
        clk_enable(sfb->bus_clk);
 
@@ -1630,39 +1649,10 @@ static int s3c_fb_runtime_resume(struct device *dev)
        pd->setup_gpio();
        writel(pd->vidcon1, sfb->regs + VIDCON1);
 
-       /* zero all windows before we do anything */
-       for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
-               s3c_fb_clear_win(sfb, win_no);
-
-       for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) {
-               void __iomem *regs = sfb->regs + sfb->variant.keycon;
-
-               regs += (win_no * 8);
-               writel(0xffffff, regs + WKEYCON0);
-               writel(0xffffff, regs + WKEYCON1);
-       }
-
-       /* restore framebuffers */
-       for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
-               win = sfb->windows[win_no];
-               if (!win)
-                       continue;
-
-               dev_dbg(&pdev->dev, "resuming window %d\n", win_no);
-               s3c_fb_set_par(win->fbinfo);
-       }
-
        return 0;
 }
-
-#else
-#define s3c_fb_suspend NULL
-#define s3c_fb_resume  NULL
-#define s3c_fb_runtime_suspend NULL
-#define s3c_fb_runtime_resume NULL
 #endif
 
-
 #define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
 #define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
 
@@ -1985,10 +1975,9 @@ static struct platform_device_id s3c_fb_driver_ids[] = {
 MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
 
 static const struct dev_pm_ops s3cfb_pm_ops = {
-       .suspend        = s3c_fb_suspend,
-       .resume         = s3c_fb_resume,
-       .runtime_suspend        = s3c_fb_runtime_suspend,
-       .runtime_resume         = s3c_fb_runtime_resume,
+       SET_SYSTEM_SLEEP_PM_OPS(s3c_fb_suspend, s3c_fb_resume)
+       SET_RUNTIME_PM_OPS(s3c_fb_runtime_suspend, s3c_fb_runtime_resume,
+                          NULL)
 };
 
 static struct platform_driver s3c_fb_driver = {
@@ -2002,18 +1991,7 @@ static struct platform_driver s3c_fb_driver = {
        },
 };
 
-static int __init s3c_fb_init(void)
-{
-       return platform_driver_register(&s3c_fb_driver);
-}
-
-static void __exit s3c_fb_cleanup(void)
-{
-       platform_driver_unregister(&s3c_fb_driver);
-}
-
-module_init(s3c_fb_init);
-module_exit(s3c_fb_cleanup);
+module_platform_driver(s3c_fb_driver);
 
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver");