]> 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: Add debug message on sync and underflow error
[sitara-epos/sitara-epos-kernel.git] / drivers / video / da8xx-fb.c
index 29577bf1f559070044ee2c9f50d300d86745f78e..9e37bf4b1ed7a2c31a09aae9e5d6b05104a65c2c 100644 (file)
 #include <linux/clk.h>
 #include <linux/cpufreq.h>
 #include <linux/console.h>
+#include <linux/spinlock.h>
 #include <linux/slab.h>
 #include <video/da8xx-fb.h>
+#include <asm/mach-types.h>
 
 #define DRIVER_NAME "da8xx_lcdc"
 
@@ -82,6 +84,8 @@
 #define LCD_V2_LIDD_CLK_EN             BIT(1)
 #define LCD_V2_CORE_CLK_EN             BIT(0)
 #define LCD_V2_LPP_B10                 26
+#define LCD_V2_TFT_24BPP_MODE          BIT(25)
+#define LCD_V2_TFT_24BPP_UNPACK                BIT(26)
 
 /* LCD Raster Timing 2 Register */
 #define LCD_AC_BIAS_TRANSITIONS_PER_INT(x)     ((x) << 16)
@@ -152,13 +156,20 @@ struct da8xx_fb_par {
        unsigned int            dma_end;
        struct clk *lcdc_clk;
        int irq;
-       unsigned short pseudo_palette[16];
+       unsigned long pseudo_palette[32];
        unsigned int palette_sz;
        unsigned int pxl_clk;
        int blank;
        wait_queue_head_t       vsync_wait;
        int                     vsync_flag;
        int                     vsync_timeout;
+       spinlock_t              lock_for_chan_update;
+
+       /*
+        * LCDC has 2 ping pong DMA channels, channel 0
+        * and channel 1.
+        */
+       unsigned int            which_dma_channel_done;
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block   freq_transition;
 #endif
@@ -174,7 +185,7 @@ static struct fb_var_screeninfo da8xx_fb_var __devinitdata = {
        .activate = 0,
        .height = -1,
        .width = -1,
-       .pixclock = 46666,      /* 46us - AUO display */
+       .pixclock = 33333,/*Pico Sec*/
        .accel_flags = 0,
        .left_margin = LEFT_MARGIN,
        .right_margin = RIGHT_MARGIN,
@@ -209,6 +220,9 @@ struct da8xx_panel {
        unsigned char   invert_pxl_clk; /* Invert Pixel clock */
 };
 
+static vsync_callback_t vsync_cb_handler;
+static void *vsync_cb_arg;
+
 static struct da8xx_panel known_lcd_panels[] = {
        /* Sharp LCD035Q3DG01 */
        [0] = {
@@ -232,12 +246,26 @@ static struct da8xx_panel known_lcd_panels[] = {
                .hfp = 2,
                .hbp = 2,
                .hsw = 41,
-               .vfp = 2,
-               .vbp = 2,
+               .vfp = 3,
+               .vbp = 3,
                .vsw = 10,
                .pxl_clk = 7833600,
                .invert_pxl_clk = 0,
        },
+       /* ThreeFive S9700RTWV35TR */
+       [2] = {
+               .name = "TFC_S9700RTWV35TR_01B",
+               .width = 800,
+               .height = 480,
+               .hfp = 39,
+               .hbp = 39,
+               .hsw = 47,
+               .vfp = 13,
+               .vbp = 29,
+               .vsw = 2,
+               .pxl_clk = 30000000,
+               .invert_pxl_clk = 0,
+       },
 };
 
 /* Enable the Raster Engine of the LCD Controller */
@@ -329,8 +357,8 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par)
        lcd_enable_raster();
 }
 
-/* Configure the Burst Size of DMA */
-static int lcd_cfg_dma(int burst_size)
+/* Configure the Burst Size and fifo threhold of DMA */
+static int lcd_cfg_dma(int burst_size,  int fifo_th)
 {
        u32 reg;
 
@@ -354,6 +382,9 @@ static int lcd_cfg_dma(int burst_size)
        default:
                return -EINVAL;
        }
+
+       reg |= (fifo_th << 8);
+
        lcdc_write(reg, LCD_DMA_CTRL_REG);
 
        return 0;
@@ -510,6 +541,13 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height,
        reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8);
        if (raster_order)
                reg |= LCD_RASTER_ORDER;
+
+       if (bpp == 24)
+               reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE);
+       else if (bpp == 32)
+               reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE
+                               | LCD_V2_TFT_24BPP_UNPACK);
+
        lcdc_write(reg, LCD_RASTER_CTRL_REG);
 
        switch (bpp) {
@@ -517,6 +555,8 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height,
        case 2:
        case 4:
        case 16:
+       case 24:
+       case 32:
                par->palette_sz = 16 * 2;
                break;
 
@@ -571,6 +611,23 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green,
 
                par->pseudo_palette[regno] = red | green | blue;
 
+               if (palette[0] != 0x4000) {
+                       update_hw = 1;
+                       palette[0] = 0x4000;
+               }
+       } else if (((info->var.bits_per_pixel == 32) && regno < 32) ||
+                   ((info->var.bits_per_pixel == 24) && regno < 24)) {
+               red >>= (24 - info->var.red.length);
+               red <<= info->var.red.offset;
+
+               green >>= (24 - info->var.green.length);
+               green <<= info->var.green.offset;
+
+               blue >>= (24 - info->var.blue.length);
+               blue <<= info->var.blue.offset;
+
+               par->pseudo_palette[regno] = red | green | blue;
+
                if (palette[0] != 0x4000) {
                        update_hw = 1;
                        palette[0] = 0x4000;
@@ -636,8 +693,8 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
                lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) &
                        ~LCD_INVERT_PIXEL_CLOCK), LCD_RASTER_TIMING_2_REG);
 
-       /* Configure the DMA burst size. */
-       ret = lcd_cfg_dma(cfg->dma_burst_sz);
+       /* Configure the DMA burst size and fifo threshold. */
+       ret = lcd_cfg_dma(cfg->dma_burst_sz, cfg->fifo_th);
        if (ret < 0)
                return ret;
 
@@ -653,7 +710,9 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
        if (ret < 0)
                return ret;
 
-       if (QVGA != cfg->p_disp_panel->panel_type)
+
+       if ((QVGA != cfg->p_disp_panel->panel_type) &&
+                       (WVGA != cfg->p_disp_panel->panel_type))
                return -EINVAL;
 
        if (cfg->bpp <= cfg->p_disp_panel->max_bpp &&
@@ -676,6 +735,32 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
        return 0;
 }
 
+int register_vsync_cb(vsync_callback_t handler, void *arg, int idx)
+{
+       if ((vsync_cb_handler == NULL) && (vsync_cb_arg == NULL)) {
+               vsync_cb_handler = handler;
+               vsync_cb_arg = arg;
+       } else {
+               return -EEXIST;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(register_vsync_cb);
+
+int unregister_vsync_cb(vsync_callback_t handler, void *arg, int idx)
+{
+       if ((vsync_cb_handler == handler) && (vsync_cb_arg == arg)) {
+               vsync_cb_handler = NULL;
+               vsync_cb_arg = NULL;
+       } else {
+               return -ENXIO;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(unregister_vsync_cb);
+
 /* IRQ handler for version 2 of LCDC */
 static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
 {
@@ -684,9 +769,12 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
        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);
                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.
@@ -709,21 +797,27 @@ static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg)
                lcdc_write(stat, LCD_MASKED_STAT_REG);
 
                if (stat & LCD_END_OF_FRAME0) {
+                       par->which_dma_channel_done = 0;
                        lcdc_write(par->dma_start,
                                   LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
                        lcdc_write(par->dma_end,
                                   LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
                        par->vsync_flag = 1;
                        wake_up_interruptible(&par->vsync_wait);
+                       if (vsync_cb_handler)
+                               vsync_cb_handler(vsync_cb_arg);
                }
 
                if (stat & LCD_END_OF_FRAME1) {
+                       par->which_dma_channel_done = 1;
                        lcdc_write(par->dma_start,
                                   LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
                        lcdc_write(par->dma_end,
                                   LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
                        par->vsync_flag = 1;
                        wake_up_interruptible(&par->vsync_wait);
+                       if (vsync_cb_handler)
+                               vsync_cb_handler(vsync_cb_arg);
                }
        }
 
@@ -739,9 +833,12 @@ static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg)
        u32 reg_ras;
 
        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);
                lcdc_write(stat, LCD_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.
@@ -822,6 +919,24 @@ static int fb_check_var(struct fb_var_screeninfo *var,
                var->transp.offset = 0;
                var->transp.length = 0;
                break;
+       case 24:
+               var->red.offset = 16;
+               var->red.length = 8;
+               var->green.offset = 8;
+               var->green.length = 8;
+               var->blue.offset = 0;
+               var->blue.length = 8;
+               break;
+       case 32:
+               var->transp.offset = 24;
+               var->transp.length = 8;
+               var->red.offset = 16;
+               var->red.length = 8;
+               var->green.offset = 8;
+               var->green.length = 8;
+               var->blue.offset = 0;
+               var->blue.length = 8;
+               break;
        default:
                err = -EINVAL;
        }
@@ -931,6 +1046,14 @@ static int fb_wait_for_vsync(struct fb_info *info)
        if (ret == 0)
                return -ETIMEDOUT;
 
+       if (par->panel_power_ctrl) {
+               /* Switch off panel power and backlight */
+               par->panel_power_ctrl(0);
+
+               /* Switch on panel power and backlight */
+               par->panel_power_ctrl(1);
+       }
+
        return 0;
 }
 
@@ -1013,6 +1136,7 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
        struct fb_fix_screeninfo    *fix = &fbi->fix;
        unsigned int end;
        unsigned int start;
+       unsigned long irq_flags;
 
        if (var->xoffset != fbi->var.xoffset ||
                        var->yoffset != fbi->var.yoffset) {
@@ -1030,12 +1154,48 @@ static int da8xx_pan_display(struct fb_var_screeninfo *var,
                        end     = start + fbi->var.yres * fix->line_length - 1;
                        par->dma_start  = start;
                        par->dma_end    = end;
+                       spin_lock_irqsave(&par->lock_for_chan_update,
+                                       irq_flags);
+                       if (par->which_dma_channel_done == 0) {
+                               lcdc_write(par->dma_start,
+                                          LCD_DMA_FRM_BUF_BASE_ADDR_0_REG);
+                               lcdc_write(par->dma_end,
+                                          LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG);
+                       } else if (par->which_dma_channel_done == 1) {
+                               lcdc_write(par->dma_start,
+                                          LCD_DMA_FRM_BUF_BASE_ADDR_1_REG);
+                               lcdc_write(par->dma_end,
+                                          LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG);
+                       }
+                       spin_unlock_irqrestore(&par->lock_for_chan_update,
+                                       irq_flags);
                }
        }
 
        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,
@@ -1056,6 +1216,7 @@ 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;
@@ -1084,6 +1245,18 @@ 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");
@@ -1100,6 +1273,7 @@ static int __devinit fb_probe(struct platform_device *device)
                lcd_revision = LCD_VERSION_1;
                break;
        case 0x4F200800:
+       case 0x4F201000:
                lcd_revision = LCD_VERSION_2;
                break;
        default:
@@ -1154,11 +1328,8 @@ 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;
 
-       par->vram_virt = dma_alloc_coherent(NULL,
-                                           par->vram_size,
-                                           (resource_size_t *) &par->vram_phys,
-                                           GFP_KERNEL | GFP_DMA);
-       if (!par->vram_virt) {
+       ret = alloc_fbmem(par);
+       if (ret) {
                dev_err(&device->dev,
                        "GLCD: kmalloc for frame buffer failed\n");
                ret = -EINVAL;
@@ -1210,6 +1381,11 @@ static int __devinit fb_probe(struct platform_device *device)
        da8xx_fb_var.hsync_len = lcdc_info->hsw;
        da8xx_fb_var.vsync_len = lcdc_info->vsw;
 
+       da8xx_fb_var.right_margin = lcdc_info->hfp;
+       da8xx_fb_var.left_margin  = lcdc_info->hbp;
+       da8xx_fb_var.lower_margin = lcdc_info->vfp;
+       da8xx_fb_var.upper_margin = lcdc_info->vbp;
+
        /* Initialize fbinfo */
        da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT;
        da8xx_fb_info->fix = da8xx_fb_fix;
@@ -1233,6 +1409,8 @@ static int __devinit fb_probe(struct platform_device *device)
        /* initialize the vsync wait queue */
        init_waitqueue_head(&par->vsync_wait);
        par->vsync_timeout = HZ / 5;
+       par->which_dma_channel_done = -1;
+       spin_lock_init(&par->lock_for_chan_update);
 
        /* Register the Frame Buffer  */
        if (register_framebuffer(da8xx_fb_info) < 0) {
@@ -1264,8 +1442,8 @@ static int __devinit fb_probe(struct platform_device *device)
 irq_freq:
 #ifdef CONFIG_CPU_FREQ
        lcd_da8xx_cpufreq_deregister(par);
-#endif
 err_cpu_freq:
+#endif
        unregister_framebuffer(da8xx_fb_info);
 
 err_dealloc_cmap:
@@ -1301,6 +1479,8 @@ 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)
@@ -1308,6 +1488,26 @@ static int fb_suspend(struct platform_device *dev, pm_message_t state)
 
        fb_set_suspend(info, 1);
        lcd_disable_raster();
+
+       /* 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);
        console_unlock();
 
@@ -1323,7 +1523,12 @@ static int fb_resume(struct platform_device *dev)
                par->panel_power_ctrl(1);
 
        clk_enable(par->lcdc_clk);
+
        lcd_enable_raster();
+
+       if (par->panel_power_ctrl)
+               par->panel_power_ctrl(1);
+
        fb_set_suspend(info, 0);
        console_unlock();