rk3188 lcdc:add vsync support
authoryxj <yxj@rock-chips.com>
Thu, 24 Jan 2013 03:05:08 +0000 (11:05 +0800)
committeryxj <yxj@rock-chips.com>
Thu, 24 Jan 2013 07:47:53 +0000 (15:47 +0800)
drivers/video/rockchip/lcdc/rk3188_lcdc.c
drivers/video/rockchip/lcdc/rk3188_lcdc.h

index c02ebf1c97ac5dae603ec5597fdcc0d63f01d69a..6bc01db5f7d0b59207f9f3b24a67fe69768427dd 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/mm.h>
 #include <linux/slab.h>
 #include <linux/device.h>
+#include <linux/kthread.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -678,6 +679,30 @@ static int rk3188_lcdc_blank(struct rk_lcdc_device_driver *dev_drv,
 
 static int rk3188_lcdc_ioctl(struct rk_lcdc_device_driver *dev_drv, unsigned int cmd,unsigned long arg,int layer_id)
 {
+       struct rk3188_lcdc_device *lcdc_dev = 
+               container_of(dev_drv,struct rk3188_lcdc_device,driver);
+       u32 panel_size[2];
+       void __user *argp = (void __user *)arg;
+       int enable;
+       switch(cmd)
+       {
+               case FBIOGET_PANEL_SIZE:    //get panel size
+                       panel_size[0] = lcdc_dev->screen->x_res;
+                       panel_size[1] = lcdc_dev->screen->y_res;
+                       if(copy_to_user(argp, panel_size, 8)) 
+                               return -EFAULT;
+                       break;
+               case RK_FBIOSET_CONFIG_DONE:
+                       lcdc_cfg_done(lcdc_dev);
+                       break;
+               case RK_FBIOSET_VSYNC_ENABLE:
+                       if (copy_from_user(&enable, argp, sizeof(enable)))
+                               return -EFAULT;
+                       lcdc_dev->vsync_info.active = enable;
+                       break;
+               default:
+                       break;
+       }
        return 0;
 }
 
@@ -992,6 +1017,35 @@ static int rk3188_set_dsp_lut(struct rk_lcdc_device_driver *dev_drv,int *lut)
        return ret;
 }
 
+
+static int lcdc_wait_for_vsync_thread(void *data)
+{
+       struct rk3188_lcdc_device  *lcdc_dev = data;
+
+       while (!kthread_should_stop()) {
+               ktime_t timestamp = lcdc_dev->vsync_info.timestamp;
+               int ret = wait_event_interruptible(lcdc_dev->vsync_info.wait,
+                       !ktime_equal(timestamp, lcdc_dev->vsync_info.timestamp) &&
+                       lcdc_dev->vsync_info.active);
+
+               if (!ret) {
+                       sysfs_notify(&lcdc_dev->driver.dev->kobj, NULL, "vsync");
+               }
+       }
+
+       return 0;
+}
+
+static ssize_t rk3188_lcdc_vsync_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct rk3188_lcdc_device *lcdc_dev = dev_get_drvdata(dev);
+       return scnprintf(buf, PAGE_SIZE, "%llu\n",
+                       ktime_to_ns(lcdc_dev->vsync_info.timestamp));
+}
+
+static DEVICE_ATTR(vsync, S_IRUGO, rk3188_lcdc_vsync_show, NULL);
+
 static struct layer_par lcdc_layer[] = {
        [0] = {
                .name           = "win0",
@@ -1031,6 +1085,7 @@ static irqreturn_t rk3188_lcdc_isr(int irq, void *dev_id)
 {
        struct rk3188_lcdc_device *lcdc_dev = 
                                (struct rk3188_lcdc_device *)dev_id;
+       ktime_t timestamp = ktime_get();
        
        lcdc_msk_reg(lcdc_dev, INT_STATUS, m_FS_INT_CLEAR, v_FS_INT_CLEAR(1));
 
@@ -1042,6 +1097,9 @@ static irqreturn_t rk3188_lcdc_isr(int irq, void *dev_id)
                spin_unlock(&(lcdc_dev->driver.cpl_lock));
        }
 #endif
+       lcdc_dev->vsync_info.timestamp = timestamp;
+       wake_up_interruptible_all(&lcdc_dev->vsync_info.wait);
+       
        return IRQ_HANDLED;
 }
 
@@ -1166,6 +1224,21 @@ static int __devinit rk3188_lcdc_probe(struct platform_device *pdev)
                ret =  -ENODEV;
                goto err4;
        }
+
+       init_waitqueue_head(&lcdc_dev->vsync_info.wait);
+       ret = device_create_file(&pdev->dev, &dev_attr_vsync);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to create vsync file\n");
+       }
+       lcdc_dev->vsync_info.thread = kthread_run(lcdc_wait_for_vsync_thread,
+               lcdc_dev, "lcdc-vsync");
+
+       
+       if (lcdc_dev->vsync_info.thread == ERR_PTR(-ENOMEM)) {
+               dev_err(&pdev->dev, "failed to run vsync thread\n");
+               lcdc_dev->vsync_info.thread = NULL;
+       }
+       lcdc_dev->vsync_info.active = 1;
        
        ret = rk_fb_register(&(lcdc_dev->driver),&lcdc_driver,lcdc_dev->id);
        if(ret < 0)
index 2426cc341db70a0681d39a1a7dbc2b4a15bea406..ac19a36238953b6f1d1954131e7f8248282bfc18 100644 (file)
@@ -305,6 +305,8 @@ struct rk3188_lcdc_device{
        
        bool clk_on;                    //if aclk or hclk is closed ,acess to register is not allowed
        u8 atv_layer_cnt;               //active layer counter,when  atv_layer_cnt = 0,disable lcdc
+       
+       struct rk_fb_vsync       vsync_info;
 
        unsigned int            irq;