]> 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 7a840d52474d44038f9d9484a4a4b0026c46ac19..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);
@@ -656,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;
 }
 
@@ -727,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 */
@@ -754,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_*
@@ -802,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) {
@@ -812,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;
@@ -825,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
@@ -847,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;
 }
@@ -872,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 */
@@ -890,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;
                }
        }
@@ -905,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;
 }
 
@@ -994,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;
 
@@ -1029,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,
@@ -1454,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;
                }
        }
 
@@ -1463,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:
@@ -1473,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);
@@ -1526,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);
@@ -1573,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 */
@@ -1591,11 +1618,40 @@ static int s3c_fb_resume(struct device *dev)
 
        return 0;
 }
-#else
-#define s3c_fb_suspend NULL
-#define s3c_fb_resume  NULL
 #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);
+
+       if (!sfb->variant.has_clksel)
+               clk_disable(sfb->lcd_clk);
+
+       clk_disable(sfb->bus_clk);
+
+       return 0;
+}
+
+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;
+
+       clk_enable(sfb->bus_clk);
+
+       if (!sfb->variant.has_clksel)
+               clk_enable(sfb->lcd_clk);
+
+       /* setup gpio and output polarity controls */
+       pd->setup_gpio();
+       writel(pd->vidcon1, sfb->regs + VIDCON1);
+
+       return 0;
+}
+#endif
 
 #define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4))
 #define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))
@@ -1918,7 +1974,11 @@ static struct platform_device_id s3c_fb_driver_ids[] = {
 };
 MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);
 
-static UNIVERSAL_DEV_PM_OPS(s3cfb_pm_ops, s3c_fb_suspend, s3c_fb_resume, NULL);
+static const struct dev_pm_ops s3cfb_pm_ops = {
+       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 = {
        .probe          = s3c_fb_probe,