Merge branch develop-3.10 into develop-3.10-next
[firefly-linux-kernel-4.4.55.git] / drivers / video / mxsfb.c
index 0b2f2dd41416c0cb17f992fcf80dd33c47871bd8..21223d475b39bb54aff2f58e95a73282f37754dd 100644 (file)
  * the required value in the imx_fb_videomode structure.
  */
 
+#include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
-#include <mach/mxsfb.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/fb.h>
+#include <linux/regulator/consumer.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
 
 #define REG_SET        4
 #define REG_CLR        8
 #define VDCTRL0_ENABLE_PRESENT         (1 << 28)
 #define VDCTRL0_VSYNC_ACT_HIGH         (1 << 27)
 #define VDCTRL0_HSYNC_ACT_HIGH         (1 << 26)
-#define VDCTRL0_DOTCLK_ACT_FAILING     (1 << 25)
+#define VDCTRL0_DOTCLK_ACT_FALLING     (1 << 25)
 #define VDCTRL0_ENABLE_ACT_HIGH                (1 << 24)
 #define VDCTRL0_VSYNC_PERIOD_UNIT      (1 << 21)
 #define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20)
 #define BLUE 2
 #define TRANSP 3
 
+#define STMLCDIF_8BIT  1 /** pixel data bus to the display is of 8 bit width */
+#define STMLCDIF_16BIT 0 /** pixel data bus to the display is of 16 bit width */
+#define STMLCDIF_18BIT 2 /** pixel data bus to the display is of 18 bit width */
+#define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */
+
+#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT        (1 << 6)
+#define MXSFB_SYNC_DOTCLK_FALLING_ACT  (1 << 7) /* negtive edge sampling */
+
 enum mxsfb_devtype {
        MXSFB_V3,
        MXSFB_V4,
@@ -164,7 +178,8 @@ struct mxsfb_info {
        unsigned ld_intf_width;
        unsigned dotclk_delay;
        const struct mxsfb_devdata *devdata;
-       int mapped;
+       u32 sync;
+       struct regulator *reg_lcd;
 };
 
 #define mxsfb_is_v3(host) (host->devdata->ipversion == 3)
@@ -324,10 +339,20 @@ static void mxsfb_enable_controller(struct fb_info *fb_info)
 {
        struct mxsfb_info *host = to_imxfb_host(fb_info);
        u32 reg;
+       int ret;
 
        dev_dbg(&host->pdev->dev, "%s\n", __func__);
 
-       clk_enable(host->clk);
+       if (host->reg_lcd) {
+               ret = regulator_enable(host->reg_lcd);
+               if (ret) {
+                       dev_err(&host->pdev->dev,
+                               "lcd regulator enable failed:   %d\n", ret);
+                       return;
+               }
+       }
+
+       clk_prepare_enable(host->clk);
        clk_set_rate(host->clk, PICOS2KHZ(fb_info->var.pixclock) * 1000U);
 
        /* if it was disabled, re-enable the mode again */
@@ -348,6 +373,7 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
        struct mxsfb_info *host = to_imxfb_host(fb_info);
        unsigned loop;
        u32 reg;
+       int ret;
 
        dev_dbg(&host->pdev->dev, "%s\n", __func__);
 
@@ -365,11 +391,19 @@ static void mxsfb_disable_controller(struct fb_info *fb_info)
                loop--;
        }
 
-       writel(VDCTRL4_SYNC_SIGNALS_ON, host->base + LCDC_VDCTRL4 + REG_CLR);
+       reg = readl(host->base + LCDC_VDCTRL4);
+       writel(reg & ~VDCTRL4_SYNC_SIGNALS_ON, host->base + LCDC_VDCTRL4);
 
-       clk_disable(host->clk);
+       clk_disable_unprepare(host->clk);
 
        host->enabled = 0;
+
+       if (host->reg_lcd) {
+               ret = regulator_disable(host->reg_lcd);
+               if (ret)
+                       dev_err(&host->pdev->dev,
+                               "lcd regulator disable failed: %d\n", ret);
+       }
 }
 
 static int mxsfb_set_par(struct fb_info *fb_info)
@@ -451,10 +485,10 @@ static int mxsfb_set_par(struct fb_info *fb_info)
                vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
        if (fb_info->var.sync & FB_SYNC_VERT_HIGH_ACT)
                vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
-       if (fb_info->var.sync & FB_SYNC_DATA_ENABLE_HIGH_ACT)
+       if (host->sync & MXSFB_SYNC_DATA_ENABLE_HIGH_ACT)
                vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
-       if (fb_info->var.sync & FB_SYNC_DOTCLK_FAILING_ACT)
-               vdctrl0 |= VDCTRL0_DOTCLK_ACT_FAILING;
+       if (host->sync & MXSFB_SYNC_DOTCLK_FALLING_ACT)
+               vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
 
        writel(vdctrl0, host->base + LCDC_VDCTRL0);
 
@@ -582,7 +616,7 @@ static struct fb_ops mxsfb_ops = {
        .fb_imageblit = cfb_imageblit,
 };
 
-static int __devinit mxsfb_restore_mode(struct mxsfb_info *host)
+static int mxsfb_restore_mode(struct mxsfb_info *host)
 {
        struct fb_info *fb_info = &host->fb_info;
        unsigned line_count;
@@ -667,20 +701,111 @@ static int __devinit mxsfb_restore_mode(struct mxsfb_info *host)
        line_count = fb_info->fix.smem_len / fb_info->fix.line_length;
        fb_info->fix.ypanstep = 1;
 
-       clk_enable(host->clk);
+       clk_prepare_enable(host->clk);
        host->enabled = 1;
 
        return 0;
 }
 
-static int __devinit mxsfb_init_fbinfo(struct mxsfb_info *host)
+static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host)
+{
+       struct fb_info *fb_info = &host->fb_info;
+       struct fb_var_screeninfo *var = &fb_info->var;
+       struct device *dev = &host->pdev->dev;
+       struct device_node *np = host->pdev->dev.of_node;
+       struct device_node *display_np;
+       struct device_node *timings_np;
+       struct display_timings *timings;
+       u32 width;
+       int i;
+       int ret = 0;
+
+       display_np = of_parse_phandle(np, "display", 0);
+       if (!display_np) {
+               dev_err(dev, "failed to find display phandle\n");
+               return -ENOENT;
+       }
+
+       ret = of_property_read_u32(display_np, "bus-width", &width);
+       if (ret < 0) {
+               dev_err(dev, "failed to get property bus-width\n");
+               goto put_display_node;
+       }
+
+       switch (width) {
+       case 8:
+               host->ld_intf_width = STMLCDIF_8BIT;
+               break;
+       case 16:
+               host->ld_intf_width = STMLCDIF_16BIT;
+               break;
+       case 18:
+               host->ld_intf_width = STMLCDIF_18BIT;
+               break;
+       case 24:
+               host->ld_intf_width = STMLCDIF_24BIT;
+               break;
+       default:
+               dev_err(dev, "invalid bus-width value\n");
+               ret = -EINVAL;
+               goto put_display_node;
+       }
+
+       ret = of_property_read_u32(display_np, "bits-per-pixel",
+                                  &var->bits_per_pixel);
+       if (ret < 0) {
+               dev_err(dev, "failed to get property bits-per-pixel\n");
+               goto put_display_node;
+       }
+
+       timings = of_get_display_timings(display_np);
+       if (!timings) {
+               dev_err(dev, "failed to get display timings\n");
+               ret = -ENOENT;
+               goto put_display_node;
+       }
+
+       timings_np = of_find_node_by_name(display_np,
+                                         "display-timings");
+       if (!timings_np) {
+               dev_err(dev, "failed to find display-timings node\n");
+               ret = -ENOENT;
+               goto put_display_node;
+       }
+
+       for (i = 0; i < of_get_child_count(timings_np); i++) {
+               struct videomode vm;
+               struct fb_videomode fb_vm;
+
+               ret = videomode_from_timings(timings, &vm, i);
+               if (ret < 0)
+                       goto put_timings_node;
+               ret = fb_videomode_from_videomode(&vm, &fb_vm);
+               if (ret < 0)
+                       goto put_timings_node;
+
+               if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
+                       host->sync |= MXSFB_SYNC_DATA_ENABLE_HIGH_ACT;
+               if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+                       host->sync |= MXSFB_SYNC_DOTCLK_FALLING_ACT;
+               fb_add_videomode(&fb_vm, &fb_info->modelist);
+       }
+
+put_timings_node:
+       of_node_put(timings_np);
+put_display_node:
+       of_node_put(display_np);
+       return ret;
+}
+
+static int mxsfb_init_fbinfo(struct mxsfb_info *host)
 {
        struct fb_info *fb_info = &host->fb_info;
        struct fb_var_screeninfo *var = &fb_info->var;
-       struct mxsfb_platform_data *pdata = host->pdev->dev.platform_data;
        dma_addr_t fb_phys;
        void *fb_virt;
-       unsigned fb_size = pdata->fb_size;
+       unsigned fb_size;
+       int ret;
 
        fb_info->fbops = &mxsfb_ops;
        fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
@@ -690,40 +815,22 @@ static int __devinit mxsfb_init_fbinfo(struct mxsfb_info *host)
        fb_info->fix.visual = FB_VISUAL_TRUECOLOR,
        fb_info->fix.accel = FB_ACCEL_NONE;
 
-       var->bits_per_pixel = pdata->default_bpp ? pdata->default_bpp : 16;
+       ret = mxsfb_init_fbinfo_dt(host);
+       if (ret)
+               return ret;
+
        var->nonstd = 0;
        var->activate = FB_ACTIVATE_NOW;
        var->accel_flags = 0;
        var->vmode = FB_VMODE_NONINTERLACED;
 
-       host->dotclk_delay = pdata->dotclk_delay;
-       host->ld_intf_width = pdata->ld_intf_width;
-
        /* Memory allocation for framebuffer */
-       if (pdata->fb_phys) {
-               if (!fb_size)
-                       return -EINVAL;
-
-               fb_phys = pdata->fb_phys;
-
-               if (!request_mem_region(fb_phys, fb_size, host->pdev->name))
-                       return -ENOMEM;
+       fb_size = SZ_2M;
+       fb_virt = alloc_pages_exact(fb_size, GFP_DMA);
+       if (!fb_virt)
+               return -ENOMEM;
 
-               fb_virt = ioremap(fb_phys, fb_size);
-               if (!fb_virt) {
-                       release_mem_region(fb_phys, fb_size);
-                       return -ENOMEM;
-               }
-               host->mapped = 1;
-       } else {
-               if (!fb_size)
-                       fb_size = SZ_2M; /* default */
-               fb_virt = alloc_pages_exact(fb_size, GFP_DMA);
-               if (!fb_virt)
-                       return -ENOMEM;
-
-               fb_phys = virt_to_phys(fb_virt);
-       }
+       fb_phys = virt_to_phys(fb_virt);
 
        fb_info->fix.smem_start = fb_phys;
        fb_info->screen_base = fb_virt;
@@ -735,32 +842,46 @@ static int __devinit mxsfb_init_fbinfo(struct mxsfb_info *host)
        return 0;
 }
 
-static void __devexit mxsfb_free_videomem(struct mxsfb_info *host)
+static void mxsfb_free_videomem(struct mxsfb_info *host)
 {
        struct fb_info *fb_info = &host->fb_info;
 
-       if (host->mapped) {
-               iounmap(fb_info->screen_base);
-               release_mem_region(fb_info->fix.smem_start,
-                               fb_info->screen_size);
-       } else {
-               free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
-       }
+       free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
 }
 
-static int __devinit mxsfb_probe(struct platform_device *pdev)
+static struct platform_device_id mxsfb_devtype[] = {
+       {
+               .name = "imx23-fb",
+               .driver_data = MXSFB_V3,
+       }, {
+               .name = "imx28-fb",
+               .driver_data = MXSFB_V4,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
+
+static const struct of_device_id mxsfb_dt_ids[] = {
+       { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
+       { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
+
+static int mxsfb_probe(struct platform_device *pdev)
 {
-       struct mxsfb_platform_data *pdata = pdev->dev.platform_data;
+       const struct of_device_id *of_id =
+                       of_match_device(mxsfb_dt_ids, &pdev->dev);
        struct resource *res;
        struct mxsfb_info *host;
        struct fb_info *fb_info;
        struct fb_modelist *modelist;
-       int i, ret;
+       struct pinctrl *pinctrl;
+       int ret;
 
-       if (!pdata) {
-               dev_err(&pdev->dev, "No platformdata. Giving up\n");
-               return -ENODEV;
-       }
+       if (of_id)
+               pdev->id_entry = of_id->data;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -768,23 +889,19 @@ static int __devinit mxsfb_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       if (!request_mem_region(res->start, resource_size(res), pdev->name))
-               return -EBUSY;
-
        fb_info = framebuffer_alloc(sizeof(struct mxsfb_info), &pdev->dev);
        if (!fb_info) {
                dev_err(&pdev->dev, "Failed to allocate fbdev\n");
-               ret = -ENOMEM;
-               goto error_alloc_info;
+               return -ENOMEM;
        }
 
        host = to_imxfb_host(fb_info);
 
-       host->base = ioremap(res->start, resource_size(res));
-       if (!host->base) {
+       host->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(host->base)) {
                dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -ENOMEM;
-               goto error_ioremap;
+               ret = PTR_ERR(host->base);
+               goto fb_release;
        }
 
        host->pdev = pdev;
@@ -792,26 +909,34 @@ static int __devinit mxsfb_probe(struct platform_device *pdev)
 
        host->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
 
-       host->clk = clk_get(&host->pdev->dev, NULL);
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               ret = PTR_ERR(pinctrl);
+               goto fb_release;
+       }
+
+       host->clk = devm_clk_get(&host->pdev->dev, NULL);
        if (IS_ERR(host->clk)) {
                ret = PTR_ERR(host->clk);
-               goto error_getclock;
+               goto fb_release;
        }
 
-       fb_info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+       host->reg_lcd = devm_regulator_get(&pdev->dev, "lcd");
+       if (IS_ERR(host->reg_lcd))
+               host->reg_lcd = NULL;
+
+       fb_info->pseudo_palette = devm_kzalloc(&pdev->dev, sizeof(u32) * 16,
+                                              GFP_KERNEL);
        if (!fb_info->pseudo_palette) {
                ret = -ENOMEM;
-               goto error_pseudo_pallette;
+               goto fb_release;
        }
 
        INIT_LIST_HEAD(&fb_info->modelist);
 
        ret = mxsfb_init_fbinfo(host);
        if (ret != 0)
-               goto error_init_fb;
-
-       for (i = 0; i < pdata->mode_count; i++)
-               fb_add_videomode(&pdata->mode_list[i], &fb_info->modelist);
+               goto fb_release;
 
        modelist = list_first_entry(&fb_info->modelist,
                        struct fb_modelist, list);
@@ -825,7 +950,7 @@ static int __devinit mxsfb_probe(struct platform_device *pdev)
        ret = register_framebuffer(fb_info);
        if (ret != 0) {
                dev_err(&pdev->dev,"Failed to register framebuffer\n");
-               goto error_register;
+               goto fb_destroy;
        }
 
        if (!host->enabled) {
@@ -838,81 +963,58 @@ static int __devinit mxsfb_probe(struct platform_device *pdev)
 
        return 0;
 
-error_register:
+fb_destroy:
        if (host->enabled)
-               clk_disable(host->clk);
+               clk_disable_unprepare(host->clk);
        fb_destroy_modelist(&fb_info->modelist);
-error_init_fb:
-       kfree(fb_info->pseudo_palette);
-error_pseudo_pallette:
-       clk_put(host->clk);
-error_getclock:
-       iounmap(host->base);
-error_ioremap:
+fb_release:
        framebuffer_release(fb_info);
-error_alloc_info:
-       release_mem_region(res->start, resource_size(res));
 
        return ret;
 }
 
-static int __devexit mxsfb_remove(struct platform_device *pdev)
+static int mxsfb_remove(struct platform_device *pdev)
 {
        struct fb_info *fb_info = platform_get_drvdata(pdev);
        struct mxsfb_info *host = to_imxfb_host(fb_info);
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        if (host->enabled)
                mxsfb_disable_controller(fb_info);
 
        unregister_framebuffer(fb_info);
-       kfree(fb_info->pseudo_palette);
        mxsfb_free_videomem(host);
-       iounmap(host->base);
-       clk_put(host->clk);
 
        framebuffer_release(fb_info);
-       release_mem_region(res->start, resource_size(res));
 
        platform_set_drvdata(pdev, NULL);
 
        return 0;
 }
 
-static struct platform_device_id mxsfb_devtype[] = {
-       {
-               .name = "imx23-fb",
-               .driver_data = MXSFB_V3,
-       }, {
-               .name = "imx28-fb",
-               .driver_data = MXSFB_V4,
-       }, {
-               /* sentinel */
-       }
-};
-MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
+static void mxsfb_shutdown(struct platform_device *pdev)
+{
+       struct fb_info *fb_info = platform_get_drvdata(pdev);
+       struct mxsfb_info *host = to_imxfb_host(fb_info);
+
+       /*
+        * Force stop the LCD controller as keeping it running during reboot
+        * might interfere with the BootROM's boot mode pads sampling.
+        */
+       writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR);
+}
 
 static struct platform_driver mxsfb_driver = {
        .probe = mxsfb_probe,
-       .remove = __devexit_p(mxsfb_remove),
+       .remove = mxsfb_remove,
+       .shutdown = mxsfb_shutdown,
        .id_table = mxsfb_devtype,
        .driver = {
                   .name = DRIVER_NAME,
+                  .of_match_table = mxsfb_dt_ids,
        },
 };
 
-static int __init mxsfb_init(void)
-{
-       return platform_driver_register(&mxsfb_driver);
-}
-
-static void __exit mxsfb_exit(void)
-{
-       platform_driver_unregister(&mxsfb_driver);
-}
-
-module_init(mxsfb_init);
-module_exit(mxsfb_exit);
+module_platform_driver(mxsfb_driver);
 
 MODULE_DESCRIPTION("Freescale mxs framebuffer driver");
 MODULE_AUTHOR("Sascha Hauer, Pengutronix");