video: rockchip: rk312x lcdc: Refine lcdc standby
authorWenlong Zhuang <daisen.zhuang@rockchips.com>
Wed, 13 May 2015 07:21:26 +0000 (15:21 +0800)
committerWenlong Zhuang <daisen.zhuang@rockchips.com>
Wed, 13 May 2015 07:21:26 +0000 (15:21 +0800)
add wait for lcdc hold valid when standby and add standby when
lcdc timing is changing at the moment of switch hdmi.

Signed-off-by: Wenlong Zhuang <daisen.zhuang@rockchips.com>
drivers/video/rockchip/lcdc/rk312x_lcdc.c
drivers/video/rockchip/lcdc/rk312x_lcdc.h
include/linux/rk_fb.h

index 86337a3583be04b82663765d017cfcaff2295617..de03d1d28e4c40b82f772530324d1f7e6f388d01 100755 (executable)
@@ -74,11 +74,16 @@ static irqreturn_t rk312x_lcdc_isr(int irq, void *dev_id)
        struct lcdc_device *lcdc_dev = (struct lcdc_device *)dev_id;
        ktime_t timestamp = ktime_get();
        u32 int_reg = lcdc_readl(lcdc_dev, INT_STATUS);
+       u32 irq_active = 0;
+
+       irq_active = int_reg & INT_STA_MSK;
+       if (irq_active)
+               lcdc_writel(lcdc_dev, INT_STATUS,
+                           int_reg | (irq_active << INT_CLR_SHIFT));
 
        if (int_reg & m_FS_INT_STA) {
                timestamp = ktime_get();
-               lcdc_msk_reg(lcdc_dev, INT_STATUS, m_FS_INT_CLEAR,
-                            v_FS_INT_CLEAR(1));
+
                /*if (lcdc_dev->driver.wait_fs) {*/
                if (0) {
                        spin_lock(&(lcdc_dev->driver.cpl_lock));
@@ -87,13 +92,18 @@ static irqreturn_t rk312x_lcdc_isr(int irq, void *dev_id)
                }
                lcdc_dev->driver.vsync_info.timestamp = timestamp;
                wake_up_interruptible_all(&lcdc_dev->driver.vsync_info.wait);
+       }
 
-       } else if (int_reg & m_LF_INT_STA) {
+       if (int_reg & m_LF_INT_STA) {
                lcdc_dev->driver.frame_time.last_framedone_t =
                                lcdc_dev->driver.frame_time.framedone_t;
                lcdc_dev->driver.frame_time.framedone_t = cpu_clock(0);
-               lcdc_msk_reg(lcdc_dev, INT_STATUS, m_LF_INT_CLEAR,
-                            v_LF_INT_CLEAR(1));
+       }
+
+       if (int_reg & m_HS_INT_STA) {
+               spin_lock(&lcdc_dev->driver.cpl_lock);
+               complete(&lcdc_dev->driver.frame_done);
+               spin_unlock(&lcdc_dev->driver.cpl_lock);
        }
 
 #ifdef LCDC_IRQ_EMPTY_DEBUG
@@ -161,9 +171,11 @@ static int rk312x_lcdc_enable_irq(struct rk_lcdc_driver *dev_drv)
        if (likely(lcdc_dev->clk_on)) {
                        mask = m_FS_INT_CLEAR | m_FS_INT_EN |
                        m_LF_INT_CLEAR | m_LF_INT_EN |
+                       m_HS_INT_CLEAR | m_HS_INT_EN |
                        m_BUS_ERR_INT_CLEAR | m_BUS_ERR_INT_EN;
                val = v_FS_INT_CLEAR(1) | v_FS_INT_EN(1) |
                        v_LF_INT_CLEAR(1) | v_LF_INT_EN(1) |
+                       v_HS_INT_CLEAR(1) | v_HS_INT_EN(1) |
                        v_BUS_ERR_INT_CLEAR(1) | v_BUS_ERR_INT_EN(0);
                #if 0
                        mask |= m_LF_INT_NUM;
@@ -193,9 +205,11 @@ static int rk312x_lcdc_disable_irq(struct lcdc_device *lcdc_dev)
        if (likely(lcdc_dev->clk_on)) {
                mask = m_FS_INT_CLEAR | m_FS_INT_EN |
                        m_LF_INT_CLEAR | m_LF_INT_EN |
+                       m_HS_INT_CLEAR | m_HS_INT_EN |
                        m_BUS_ERR_INT_CLEAR | m_BUS_ERR_INT_EN;
                val = v_FS_INT_CLEAR(0) | v_FS_INT_EN(0) |
                        v_LF_INT_CLEAR(0) | v_LF_INT_EN(0) |
+                       v_HS_INT_CLEAR(0) | v_HS_INT_EN(0) |
                        v_BUS_ERR_INT_CLEAR(0) | v_BUS_ERR_INT_EN(0);
 #ifdef LCDC_IRQ_EMPTY_DEBUG
                mask |= m_WIN0_EMPTY_INT_EN | m_WIN1_EMPTY_INT_EN;
@@ -716,6 +730,44 @@ static int rk312x_lcdc_set_dclk(struct rk_lcdc_driver *dev_drv,
        return 0;
 }
 
+static int rk312x_lcdc_standby(struct rk_lcdc_driver *dev_drv, bool enable)
+{
+       struct lcdc_device *vop_dev =
+               container_of(dev_drv, struct lcdc_device, driver);
+       int timeout;
+       unsigned long flags;
+
+       if (unlikely(!vop_dev->clk_on))
+               return 0;
+
+       if (dev_drv->standby && !enable) {
+               dev_drv->standby = 0;
+               lcdc_msk_reg(vop_dev, SYS_CTRL, m_LCDC_STANDBY,
+                            v_LCDC_STANDBY(0));
+               return 0;
+       } else if (!dev_drv->standby && enable) {
+               spin_lock_irqsave(&dev_drv->cpl_lock, flags);
+               init_completion(&dev_drv->frame_done);
+               spin_unlock_irqrestore(&dev_drv->cpl_lock, flags);
+
+               lcdc_msk_reg(vop_dev, SYS_CTRL, m_LCDC_STANDBY,
+                            v_LCDC_STANDBY(1));
+               /* wait for standby hold valid */
+               timeout = wait_for_completion_timeout(&dev_drv->frame_done,
+                                                     msecs_to_jiffies(25));
+
+               if (!timeout && (!dev_drv->frame_done.done)) {
+                       dev_info(dev_drv->dev,
+                                "wait for standy hold valid start time out!\n");
+                       return -ETIMEDOUT;
+               }
+
+               dev_drv->standby = 1;
+       }
+
+       return 0;
+}
+
 /********do basic init*********/
 static int rk312x_lcdc_pre_init(struct rk_lcdc_driver *dev_drv)
 {
@@ -1137,10 +1189,6 @@ static int rk312x_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
 
        spin_lock(&lcdc_dev->reg_lock);
        if (likely(lcdc_dev->clk_on)) {
-               lcdc_msk_reg(lcdc_dev, SYS_CTRL,
-                            m_LCDC_STANDBY, v_LCDC_STANDBY(1));
-               lcdc_cfg_done(lcdc_dev);
-               mdelay(50);
                /* Select output color domain */
                /*dev_drv->output_color = screen->color_mode;
                if (lcdc_dev->soc_type == VOP_RK312X) {
@@ -1412,9 +1460,10 @@ static int rk312x_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen)
                }
        }
        spin_unlock(&lcdc_dev->reg_lock);
+
        rk312x_lcdc_set_dclk(dev_drv, 1);
-       lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_LCDC_STANDBY, v_LCDC_STANDBY(0));
        lcdc_cfg_done(lcdc_dev);
+
        if (dev_drv->trsm_ops && dev_drv->trsm_ops->enable)
                dev_drv->trsm_ops->enable();
        if (screen->init)
@@ -1459,7 +1508,9 @@ static int rk312x_lcdc_open(struct rk_lcdc_driver *dev_drv, int win_id,
                        rk312x_lcdc_set_dclk(dev_drv, 0);
                        rk312x_lcdc_enable_irq(dev_drv);
                } else {
+                       rk312x_lcdc_standby(dev_drv, true);
                        rk312x_load_screen(dev_drv, 1);
+                       rk312x_lcdc_standby(dev_drv, false);
                }
 
                /* set screen lut */
@@ -2466,6 +2517,8 @@ static int rk312x_lcdc_dsp_black(struct rk_lcdc_driver *dev_drv, int enable)
                }
                spin_unlock(&lcdc_dev->reg_lock);
 
+               rk312x_lcdc_standby(dev_drv, true);
+
                if (dev_drv->trsm_ops && dev_drv->trsm_ops->disable)
                        dev_drv->trsm_ops->disable();
        } else {
@@ -2476,8 +2529,12 @@ static int rk312x_lcdc_dsp_black(struct rk_lcdc_driver *dev_drv, int enable)
                        lcdc_cfg_done(lcdc_dev);
                }
                spin_unlock(&lcdc_dev->reg_lock);
+
                if (dev_drv->trsm_ops && dev_drv->trsm_ops->enable)
                        dev_drv->trsm_ops->enable();
+
+               rk312x_lcdc_standby(dev_drv, false);
+
                msleep(100);
                /* open the backlight */
                if (lcdc_dev->backlight) {
@@ -2696,6 +2753,7 @@ static void rk312x_lcdc_shutdown(struct platform_device *pdev)
        flush_kthread_worker(&dev_drv->update_regs_worker);
        kthread_stop(dev_drv->update_regs_thread);
 
+       rk312x_lcdc_standby(dev_drv, true);
        rk312x_lcdc_deinit(lcdc_dev);
        rk312x_lcdc_clk_disable(lcdc_dev);
        rk_disp_pwr_disable(&lcdc_dev->driver);
index 1d3dcc8f164da8ee1bb12ff5c069cda269d0d1ce..ec1d3cece51b4213e80cd166ecd7e3985c36f717 100755 (executable)
@@ -649,6 +649,9 @@ enum _vop_overlay_mode {
 
 
 #define CalScale(x, y)              ((((u32)(x - 1)) * 0x1000) / (y - 1))
+#define INT_STA_MSK    (m_HS_INT_STA | m_FS_INT_STA |          \
+                        m_LF_INT_STA | m_BUS_ERR_INT_STA)
+#define INT_CLR_SHIFT  8
 
 struct rk_lcdc_drvdata {
      u8 soc_type;
index 6cc5c923682398c7a798578fe682c8b64af69404..2b8fc3ab5d8f1fa4f42cb57b073f5314ec88749c 100755 (executable)
@@ -627,6 +627,7 @@ struct rk_lcdc_driver {
        struct sw_sync_timeline *timeline;
        int                     timeline_max;
        int                     suspend_flag;
+       int standby;
        struct list_head        update_regs_list;
        struct list_head        saved_list;
        struct mutex            update_regs_list_lock;