Merge branch 'develop-3.0' of ssh://10.10.10.29/rk/kernel into develop-3.0
[firefly-linux-kernel-4.4.55.git] / drivers / video / imxfb.c
index 66358fa825f3888b763a4e513fece8869c6f602f..f135dbead07d8921068de3513dafb2c32ea10973 100644 (file)
  */
 #define DEBUG_VAR 1
 
+#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
+       (defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE) && \
+               defined(CONFIG_FB_IMX_MODULE))
+#define PWMR_BACKLIGHT_AVAILABLE
+#endif
+
 #define DRIVER_NAME "imx-fb"
 
 #define LCDC_SSA       0x00
 #define LCDC_SIZE      0x04
 #define SIZE_XMAX(x)   ((((x) >> 4) & 0x3f) << 20)
 
-#ifdef CONFIG_ARCH_MX1
-#define SIZE_YMAX(y)   ((y) & 0x1ff)
-#else
-#define SIZE_YMAX(y)   ((y) & 0x3ff)
-#endif
+#define YMAX_MASK       (cpu_is_mx1() ? 0x1ff : 0x3ff)
+#define SIZE_YMAX(y)   ((y) & YMAX_MASK)
 
 #define LCDC_VPW       0x08
 #define VPW_VPW(x)     ((x) & 0x3ff)
 #define CPOS_OP                (1<<28)
 #define CPOS_CXP(x)    (((x) & 3ff) << 16)
 
-#ifdef CONFIG_ARCH_MX1
-#define CPOS_CYP(y)    ((y) & 0x1ff)
-#else
-#define CPOS_CYP(y)    ((y) & 0x3ff)
-#endif
-
 #define LCDC_LCWHB     0x10
 #define LCWHB_BK_EN    (1<<31)
 #define LCWHB_CW(w)    (((w) & 0x1f) << 24)
 
 #define LCDC_LCHCC     0x14
 
-#ifdef CONFIG_ARCH_MX1
-#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
-#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
-#define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
-#else
-#define LCHCC_CUR_COL_R(r) (((r) & 0x3f) << 12)
-#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 6)
-#define LCHCC_CUR_COL_B(b) ((b) & 0x3f)
-#endif
-
 #define LCDC_PCR       0x18
 
 #define LCDC_HCR       0x1C
 
 #define LCDC_RMCR      0x34
 
-#ifdef CONFIG_ARCH_MX1
-#define RMCR_LCDC_EN   (1<<1)
-#else
-#define RMCR_LCDC_EN   0
-#endif
+#define RMCR_LCDC_EN_MX1       (1<<1)
 
 #define RMCR_SELF_REF  (1<<0)
 
@@ -175,6 +158,9 @@ struct imxfb_info {
 
        struct imx_fb_videomode *mode;
        int                     num_modes;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+       struct backlight_device *bl;
+#endif
 
        void (*lcd_power)(int);
        void (*backlight_power)(int);
@@ -449,6 +435,74 @@ static int imxfb_set_par(struct fb_info *info)
        return 0;
 }
 
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+static int imxfb_bl_get_brightness(struct backlight_device *bl)
+{
+       struct imxfb_info *fbi = bl_get_data(bl);
+
+       return readl(fbi->regs + LCDC_PWMR) & 0xFF;
+}
+
+static int imxfb_bl_update_status(struct backlight_device *bl)
+{
+       struct imxfb_info *fbi = bl_get_data(bl);
+       int brightness = bl->props.brightness;
+
+       if (bl->props.power != FB_BLANK_UNBLANK)
+               brightness = 0;
+       if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+               brightness = 0;
+
+       fbi->pwmr = (fbi->pwmr & ~0xFF) | brightness;
+
+       if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+               clk_enable(fbi->clk);
+       writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+       if (bl->props.fb_blank != FB_BLANK_UNBLANK)
+               clk_disable(fbi->clk);
+
+       return 0;
+}
+
+static const struct backlight_ops imxfb_lcdc_bl_ops = {
+       .update_status = imxfb_bl_update_status,
+       .get_brightness = imxfb_bl_get_brightness,
+};
+
+static void imxfb_init_backlight(struct imxfb_info *fbi)
+{
+       struct backlight_properties props;
+       struct backlight_device *bl;
+
+       if (fbi->bl)
+               return;
+
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.max_brightness = 0xff;
+       props.type = BACKLIGHT_RAW;
+       writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+
+       bl = backlight_device_register("imxfb-bl", &fbi->pdev->dev, fbi,
+                                      &imxfb_lcdc_bl_ops, &props);
+       if (IS_ERR(bl)) {
+               dev_err(&fbi->pdev->dev, "error %ld on backlight register\n",
+                               PTR_ERR(bl));
+               return;
+       }
+
+       fbi->bl = bl;
+       bl->props.power = FB_BLANK_UNBLANK;
+       bl->props.fb_blank = FB_BLANK_UNBLANK;
+       bl->props.brightness = imxfb_bl_get_brightness(bl);
+}
+
+static void imxfb_exit_backlight(struct imxfb_info *fbi)
+{
+       if (fbi->bl)
+               backlight_device_unregister(fbi->bl);
+}
+#endif
+
 static void imxfb_enable_controller(struct imxfb_info *fbi)
 {
        pr_debug("Enabling LCD controller\n");
@@ -462,7 +516,11 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
        writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
                fbi->regs + LCDC_CPOS);
 
-       writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR);
+       /*
+        * RMCR_LCDC_EN_MX1 is present on i.MX1 only, but doesn't hurt
+        * on other SoCs
+        */
+       writel(RMCR_LCDC_EN_MX1, fbi->regs + LCDC_RMCR);
 
        clk_enable(fbi->clk);
 
@@ -547,7 +605,7 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
        if (var->right_margin > 255)
                printk(KERN_ERR "%s: invalid right_margin %d\n",
                        info->fix.id, var->right_margin);
-       if (var->yres < 1 || var->yres > 511)
+       if (var->yres < 1 || var->yres > YMAX_MASK)
                printk(KERN_ERR "%s: invalid yres %d\n",
                        info->fix.id, var->yres);
        if (var->vsync_len > 100)
@@ -579,7 +637,9 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
                        fbi->regs + LCDC_SIZE);
 
        writel(fbi->pcr, fbi->regs + LCDC_PCR);
+#ifndef PWMR_BACKLIGHT_AVAILABLE
        writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
+#endif
        writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
        writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
 
@@ -593,7 +653,8 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
  */
 static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct imxfb_info *fbi = platform_get_drvdata(dev);
+       struct fb_info *info = platform_get_drvdata(dev);
+       struct imxfb_info *fbi = info->par;
 
        pr_debug("%s\n", __func__);
 
@@ -603,7 +664,8 @@ static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
 
 static int imxfb_resume(struct platform_device *dev)
 {
-       struct imxfb_info *fbi = platform_get_drvdata(dev);
+       struct fb_info *info = platform_get_drvdata(dev);
+       struct imxfb_info *fbi = info->par;
 
        pr_debug("%s\n", __func__);
 
@@ -777,6 +839,10 @@ static int __init imxfb_probe(struct platform_device *pdev)
        }
 
        imxfb_enable_controller(fbi);
+       fbi->pdev = pdev;
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+       imxfb_init_backlight(fbi);
+#endif
 
        return 0;
 
@@ -790,10 +856,10 @@ failed_platform_init:
                dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
                        fbi->map_dma);
 failed_map:
-       clk_put(fbi->clk);
-failed_getclock:
        iounmap(fbi->regs);
 failed_ioremap:
+       clk_put(fbi->clk);
+failed_getclock:
        release_mem_region(res->start, resource_size(res));
 failed_req:
        kfree(info->pseudo_palette);
@@ -814,6 +880,9 @@ static int __devexit imxfb_remove(struct platform_device *pdev)
 
        imxfb_disable_controller(fbi);
 
+#ifdef PWMR_BACKLIGHT_AVAILABLE
+       imxfb_exit_backlight(fbi);
+#endif
        unregister_framebuffer(info);
 
        pdata = pdev->dev.platform_data;
@@ -890,6 +959,6 @@ static void __exit imxfb_cleanup(void)
 module_init(imxfb_init);
 module_exit(imxfb_cleanup);
 
-MODULE_DESCRIPTION("Motorola i.MX framebuffer driver");
+MODULE_DESCRIPTION("Freescale i.MX framebuffer driver");
 MODULE_AUTHOR("Sascha Hauer, Pengutronix");
 MODULE_LICENSE("GPL");