]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - sitara-epos/sitara-epos-kernel.git/blobdiff - drivers/video/da8xx-fb.c
video: da8xx-fb: correct suspend/resume sequence
[sitara-epos/sitara-epos-kernel.git] / drivers / video / da8xx-fb.c
index 43c3f6ebaad6a9b5dd049fbfacef7d8156c15262..5736a3ec107ef45b96eb77f070f973dc3344d25a 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/console.h>
 #include <linux/spinlock.h>
 #include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <video/da8xx-fb.h>
 #include <asm/mach-types.h>
 
 #define RIGHT_MARGIN   64
 #define UPPER_MARGIN   32
 #define LOWER_MARGIN   32
+#define WAIT_FOR_FRAME_DONE    true
+#define NO_WAIT_FOR_FRAME_DONE false
 
 static resource_size_t da8xx_fb_reg_base;
 static struct resource *lcdc_regs;
@@ -138,15 +142,16 @@ static irq_handler_t lcdc_irq_handler;
 
 static inline unsigned int lcdc_read(unsigned int addr)
 {
-       return (unsigned int)__raw_readl(da8xx_fb_reg_base + (addr));
+       return (unsigned int)readl(da8xx_fb_reg_base + (addr));
 }
 
 static inline void lcdc_write(unsigned int val, unsigned int addr)
 {
-       __raw_writel(val, da8xx_fb_reg_base + (addr));
+       writel(val, da8xx_fb_reg_base + (addr));
 }
 
 struct da8xx_fb_par {
+       struct device *dev;
        resource_size_t p_palette_base;
        unsigned char *v_palette_base;
        dma_addr_t              vram_phys;
@@ -284,17 +289,39 @@ static inline void lcd_enable_raster(void)
 }
 
 /* Disable the Raster Engine of the LCD Controller */
-static inline void lcd_disable_raster(void)
+static inline void lcd_disable_raster(bool wait_for_frame_done)
 {
        u32 reg;
+       u32 loop_cnt = 0;
+       u32 stat;
+       u32 i = 0;
+
+       if (wait_for_frame_done)
+               loop_cnt = 5000;
 
        reg = lcdc_read(LCD_RASTER_CTRL_REG);
        if (reg & LCD_RASTER_ENABLE)
                lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
 
-       if (lcd_revision == LCD_VERSION_2)
-               /* Write 1 to reset LCDC */
-               lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
+       /* Wait for the current frame to complete */
+       do {
+               if (lcd_revision == LCD_VERSION_1)
+                       stat = lcdc_read(LCD_STAT_REG);
+               else
+                       stat = lcdc_read(LCD_RAW_STAT_REG);
+
+               mdelay(1);
+       } while (!(stat & BIT(0)) && (i++ < loop_cnt));
+
+       if (lcd_revision == LCD_VERSION_1)
+               lcdc_write(stat, LCD_STAT_REG);
+       else
+               lcdc_write(stat, LCD_MASKED_STAT_REG);
+
+       if ((loop_cnt != 0) && (i >= loop_cnt)) {
+               printk(KERN_ERR "LCD Controller timed out\n");
+               return;
+       }
 }
 
 static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
@@ -321,7 +348,8 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
                } else {
                        reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) |
                                LCD_V2_END_OF_FRAME0_INT_ENA |
-                               LCD_V2_END_OF_FRAME1_INT_ENA;
+                               LCD_V2_END_OF_FRAME1_INT_ENA |
+                               LCD_V2_UNDERFLOW_INT_ENA | LCD_SYNC_LOST;
                        lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG);
                }
                reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE;
@@ -645,7 +673,7 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green,
 static void lcd_reset(struct da8xx_fb_par *par)
 {
        /* Disable the Raster if previously Enabled */
-       lcd_disable_raster();
+       lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
 
        /* DMA has to be disabled */
        lcdc_write(0, LCD_DMA_CTRL_REG);
@@ -767,15 +795,14 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
 {
        struct da8xx_fb_par *par = arg;
        u32 stat = lcdc_read(LCD_MASKED_STAT_REG);
+       struct device *dev = par->dev;
        u32 reg_int;
 
        if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
                printk(KERN_ERR "LCDC sync lost or underflow error occured\n");
-               lcd_disable_raster();
-               clk_disable(par->lcdc_clk);
+               lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
                lcdc_write(stat, LCD_MASKED_STAT_REG);
                lcd_enable_raster();
-               clk_enable(par->lcdc_clk);
        } else if (stat & LCD_PL_LOAD_DONE) {
                /*
                 * Must disable raster before changing state of any control bit.
@@ -783,7 +810,7 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
                 * interrupt via the following write to the status register. If
                 * this is done after then one gets multiple PL done interrupts.
                 */
-               lcd_disable_raster();
+               lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
 
                lcdc_write(stat, LCD_MASKED_STAT_REG);
 
@@ -835,7 +862,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
 
        if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
                printk(KERN_ERR "LCDC sync lost or underflow error occured\n");
-               lcd_disable_raster();
+               lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
                clk_disable(par->lcdc_clk);
                lcdc_write(stat, LCD_STAT_REG);
                lcd_enable_raster();
@@ -847,7 +874,7 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
                 * interrupt via the following write to the status register. If
                 * this is done after then one gets multiple PL done interrupts.
                 */
-               lcd_disable_raster();
+               lcd_disable_raster(NO_WAIT_FOR_FRAME_DONE);
 
                lcdc_write(stat, LCD_STAT_REG);
 
@@ -958,7 +985,7 @@ static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb,
        par = container_of(nb, struct da8xx_fb_par, freq_transition);
        if (val == CPUFREQ_POSTCHANGE) {
                if (par->lcd_fck_rate != clk_get_rate(par->lcdc_clk)) {
-                       lcd_disable_raster();
+                       lcd_disable_raster(WAIT_FOR_FRAME_DONE);
                        lcd_calc_clk_divider(par);
                        lcd_enable_raster();
                }
@@ -995,7 +1022,7 @@ static int __devexit fb_remove(struct platform_device *dev)
                if (par->panel_power_ctrl)
                        par->panel_power_ctrl(0);
 
-               lcd_disable_raster();
+               lcd_disable_raster(WAIT_FOR_FRAME_DONE);
                lcdc_write(0, LCD_RASTER_CTRL_REG);
 
                /* disable DMA  */
@@ -1008,8 +1035,8 @@ static int __devexit fb_remove(struct platform_device *dev)
                dma_free_coherent(NULL, par->vram_size, par->vram_virt,
                                  par->vram_phys);
                free_irq(par->irq, par);
-               clk_disable(par->lcdc_clk);
-               clk_put(par->lcdc_clk);
+               pm_runtime_put_sync(&dev->dev);
+               pm_runtime_disable(&dev->dev);
                framebuffer_release(info);
                iounmap((void __iomem *)da8xx_fb_reg_base);
                release_mem_region(lcdc_regs->start, resource_size(lcdc_regs));
@@ -1116,7 +1143,7 @@ static int cfb_blank(int blank, struct fb_info *info)
                if (par->panel_power_ctrl)
                        par->panel_power_ctrl(0);
 
-               lcd_disable_raster();
+               lcd_disable_raster(WAIT_FOR_FRAME_DONE);
                break;
        default:
                ret = -EINVAL;
@@ -1177,27 +1204,6 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
        return ret;
 }
 
-static int alloc_fbmem(struct da8xx_fb_par *par)
-{
-       unsigned int order, size;
-       unsigned long addr;
-
-       size = PAGE_ALIGN(par->vram_size);
-       order = get_order(size);
-       par->vram_virt = (void*) __get_free_pages(GFP_KERNEL, order);
-       addr = (unsigned long) par->vram_virt;
-       if (addr) {
-               while (size > 0) {
-                       SetPageReserved(virt_to_page(addr));
-                       addr += PAGE_SIZE;
-                       size -= PAGE_SIZE;
-               }
-               par->vram_phys = (u32) virt_to_phys((void *) par->vram_virt);
-               return 0;
-       }
-       return -ENOMEM;
-}
-
 static struct fb_ops da8xx_fb_ops = {
        .owner = THIS_MODULE,
        .fb_check_var = fb_check_var,
@@ -1218,7 +1224,6 @@ static int __devinit fb_probe(struct platform_device *device)
        struct da8xx_panel *lcdc_info;
        struct fb_info *da8xx_fb_info;
        struct clk *fb_clk = NULL;
-       struct clk *lcdc_ick = NULL;
        struct da8xx_fb_par *par;
        resource_size_t len;
        int ret, i;
@@ -1247,27 +1252,17 @@ static int __devinit fb_probe(struct platform_device *device)
                goto err_request_mem;
        }
 
-       /*
-        * Some SoC will not have seperate interface clock,
-        * so make lazy check here
-        */
-       lcdc_ick = clk_get(&device->dev, "lcdc_ick");
-       if (IS_ERR(lcdc_ick))
-               dev_err(&device->dev, "Can not get lcdc_ick\n");
-
-       ret = clk_enable(lcdc_ick);
-       if (ret)
-               dev_err(&device->dev, "failed to enable lcdc_ick\n");
-
        fb_clk = clk_get(&device->dev, NULL);
        if (IS_ERR(fb_clk)) {
                dev_err(&device->dev, "Can not get device clock\n");
                ret = -ENODEV;
                goto err_ioremap;
        }
-       ret = clk_enable(fb_clk);
-       if (ret)
-               goto err_clk_put;
+
+       pm_runtime_irq_safe(&device->dev);
+       pm_runtime_enable(&device->dev);
+       pm_runtime_get_sync(&device->dev);
+
 
        /* Determine LCD IP Version */
        switch (lcdc_read(LCD_PID_REG)) {
@@ -1296,7 +1291,7 @@ static int __devinit fb_probe(struct platform_device *device)
        if (i == ARRAY_SIZE(known_lcd_panels)) {
                dev_err(&device->dev, "GLCD: No valid panel found\n");
                ret = -ENODEV;
-               goto err_clk_disable;
+               goto err_pm_runtime_disable;
        } else
                dev_info(&device->dev, "GLCD: Found %s panel\n",
                                        fb_pdata->type);
@@ -1308,10 +1303,11 @@ static int __devinit fb_probe(struct platform_device *device)
        if (!da8xx_fb_info) {
                dev_dbg(&device->dev, "Memory allocation failed for fb_info\n");
                ret = -ENOMEM;
-               goto err_clk_disable;
+               goto err_pm_runtime_disable;
        }
 
        par = da8xx_fb_info->par;
+       par->dev = &device->dev;
        par->lcdc_clk = fb_clk;
 #ifdef CONFIG_CPU_FREQ
        par->lcd_fck_rate = clk_get_rate(fb_clk);
@@ -1333,8 +1329,11 @@ static int __devinit fb_probe(struct platform_device *device)
        par->vram_size = PAGE_ALIGN(par->vram_size/8);
        par->vram_size = par->vram_size * LCD_NUM_BUFFERS;
 
-       ret = alloc_fbmem(par);
-       if (ret) {
+       par->vram_virt = dma_alloc_coherent(NULL,
+                                           par->vram_size,
+                                           (resource_size_t *) &par->vram_phys,
+                                           GFP_KERNEL | GFP_DMA);
+       if (!par->vram_virt) {
                dev_err(&device->dev,
                        "GLCD: kmalloc for frame buffer failed\n");
                ret = -EINVAL;
@@ -1464,13 +1463,12 @@ err_release_fb_mem:
 err_release_fb:
        framebuffer_release(da8xx_fb_info);
 
-err_clk_disable:
-       clk_disable(fb_clk);
-
-err_clk_put:
-       clk_put(fb_clk);
+err_pm_runtime_disable:
+       pm_runtime_put_sync(&device->dev);
+       pm_runtime_disable(&device->dev);
 
 err_ioremap:
+
        iounmap((void __iomem *)da8xx_fb_reg_base);
 
 err_request_mem:
@@ -1480,40 +1478,80 @@ err_request_mem:
 }
 
 #ifdef CONFIG_PM
+
+struct lcdc_context {
+       u32 clk_enable;
+       u32 ctrl;
+       u32 dma_ctrl;
+       u32 raster_timing_0;
+       u32 raster_timing_1;
+       u32 raster_timing_2;
+       u32 int_enable_set;
+       u32 dma_frm_buf_base_addr_0;
+       u32 dma_frm_buf_ceiling_addr_0;
+       u32 dma_frm_buf_base_addr_1;
+       u32 dma_frm_buf_ceiling_addr_1;
+       u32 raster_ctrl;
+} reg_context;
+
+static void lcd_context_save(void)
+{
+       reg_context.clk_enable = lcdc_read(LCD_CLK_ENABLE_REG);
+       reg_context.ctrl = lcdc_read(LCD_CTRL_REG);
+       reg_context.dma_ctrl = lcdc_read(LCD_DMA_CTRL_REG);
+       reg_context.raster_timing_0 = lcdc_read(LCD_RASTER_TIMING_0_REG);
+       reg_context.raster_timing_1 = lcdc_read(LCD_RASTER_TIMING_1_REG);
+       reg_context.raster_timing_2 = lcdc_read(LCD_RASTER_TIMING_2_REG);
+       reg_context.int_enable_set = lcdc_read(LCD_INT_ENABLE_SET_REG);
+       reg_context.dma_frm_buf_base_addr_0 =
+               lcdc_read(LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+       reg_context.dma_frm_buf_ceiling_addr_0 =
+               lcdc_read(LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+       reg_context.dma_frm_buf_base_addr_1 =
+               lcdc_read(LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+       reg_context.dma_frm_buf_ceiling_addr_1 =
+               lcdc_read(LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+       reg_context.raster_ctrl = lcdc_read(LCD_RASTER_CTRL_REG) &
+               ~LCD_RASTER_ENABLE;
+       return;
+}
+
+static void lcd_context_restore(void)
+{
+       lcdc_write(reg_context.clk_enable, LCD_CLK_ENABLE_REG);
+       lcdc_write(reg_context.ctrl, LCD_CTRL_REG);
+       lcdc_write(reg_context.dma_ctrl, LCD_DMA_CTRL_REG);
+       lcdc_write(reg_context.raster_timing_0, LCD_RASTER_TIMING_0_REG);
+       lcdc_write(reg_context.raster_timing_1, LCD_RASTER_TIMING_1_REG);
+       lcdc_write(reg_context.raster_timing_2, LCD_RASTER_TIMING_2_REG);
+       lcdc_write(reg_context.int_enable_set, LCD_INT_ENABLE_SET_REG);
+       lcdc_write(reg_context.dma_frm_buf_base_addr_0,
+                       LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+       lcdc_write(reg_context.dma_frm_buf_ceiling_addr_0,
+                       LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+       lcdc_write(reg_context.dma_frm_buf_base_addr_1,
+                       LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+       lcdc_write(reg_context.dma_frm_buf_ceiling_addr_1,
+                       LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+       lcdc_write(reg_context.raster_ctrl, LCD_RASTER_CTRL_REG);
+       return;
+}
+
 static int fb_suspend(struct platform_device *dev, pm_message_t state)
 {
        struct fb_info *info = platform_get_drvdata(dev);
        struct da8xx_fb_par *par = info->par;
-       unsigned long timeo = jiffies + msecs_to_jiffies(5000);
-       u32 stat;
 
        console_lock();
        if (par->panel_power_ctrl)
                par->panel_power_ctrl(0);
 
        fb_set_suspend(info, 1);
-       lcd_disable_raster();
+       lcd_context_save();
+       lcd_disable_raster(WAIT_FOR_FRAME_DONE);
+       msleep(10);
 
-       /* Wait for the current frame to complete */
-       do {
-               if (lcd_revision == LCD_VERSION_1)
-                       stat = lcdc_read(LCD_STAT_REG);
-               else
-                       stat = lcdc_read(LCD_MASKED_STAT_REG);
-               cpu_relax();
-       } while (!(stat & BIT(0)) && time_before(jiffies, timeo));
-
-       if (lcd_revision == LCD_VERSION_1)
-               lcdc_write(stat, LCD_STAT_REG);
-       else
-               lcdc_write(stat, LCD_MASKED_STAT_REG);
-
-       if (time_after_eq(jiffies, timeo)) {
-               dev_err(&dev->dev, "controller timed out\n");
-               return -ETIMEDOUT;
-       }
-
-       clk_disable(par->lcdc_clk);
+       pm_runtime_put(&dev->dev);
        console_unlock();
 
        return 0;
@@ -1524,11 +1562,16 @@ static int fb_resume(struct platform_device *dev)
        struct da8xx_fb_par *par = info->par;
 
        console_lock();
-       if (par->panel_power_ctrl)
-               par->panel_power_ctrl(1);
 
-       clk_enable(par->lcdc_clk);
+       pm_runtime_get_sync(&dev->dev);
+
+       msleep(1);
+       lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG);
+       msleep(10);
+       lcdc_write(0, LCD_CLK_RESET_REG);
+       msleep(1);
 
+       lcd_context_restore();
        lcd_enable_raster();
 
        if (par->panel_power_ctrl)