video: tegra: dc: support for vblank sync
Rakesh Iyer [Wed, 22 Aug 2012 01:25:17 +0000 (18:25 -0700)]
Add wait for vsync support for one-shot panels. The code supports extension
of this feature to other panels.

Bug 1033411.

Change-Id: Ie4d6cb45e5de81083458169ccdfa33230235ed72
Signed-off-by: Rakesh Iyer <riyer@nvidia.com>
Reviewed-on: http://git-master/r/140769
Reviewed-on: http://git-master/r/140766
Signed-off-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-on: http://git-master/r/147614
Reviewed-by: Automatic_Commit_Validation_User

Rebase-Id: Rf4e58f6f43b93ff3a6e4b3bdc70101e336b38403

arch/arm/mach-tegra/include/mach/dc.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_sysfs.c
drivers/video/tegra/dc/dsi.c
drivers/video/tegra/dc/dsi.h
drivers/video/tegra/fb.c

index 1268394..2696fc8 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <linux/pm.h>
 #include <linux/types.h>
+#include <linux/fb.h>
 #include <drm/drm_fixed.h>
 
 #define TEGRA_MAX_DC           2
@@ -481,6 +482,8 @@ struct tegra_dc_out {
 
        u8                      *out_sel_configs;
        unsigned                n_out_sel_configs;
+       bool                    user_needs_vblank;
+       struct completion       user_vblank_comp;
 
        int     (*enable)(struct device *);
        int     (*postpoweron)(void);
@@ -664,6 +667,8 @@ bool tegra_dc_get_connected(struct tegra_dc *);
 bool tegra_dc_hpd(struct tegra_dc *dc);
 
 
+void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank);
+int tegra_dc_wait_for_vsync(struct tegra_dc *dc);
 void tegra_dc_blank(struct tegra_dc *dc);
 
 void tegra_dc_enable(struct tegra_dc *dc);
index 7ab6c48..0dc8ece 100644 (file)
@@ -1176,6 +1176,36 @@ static inline void enable_dc_irq(unsigned int irq)
 #endif
 }
 
+void tegra_dc_get_fbvblank(struct tegra_dc *dc, struct fb_vblank *vblank)
+{
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               vblank->flags = FB_VBLANK_HAVE_VSYNC;
+}
+
+int tegra_dc_wait_for_vsync(struct tegra_dc *dc)
+{
+       int ret = -ENOTTY;
+
+       if (!(dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) || !dc->enabled)
+               return ret;
+
+       /*
+        * Logic is as follows
+        * a) Indicate we need a vblank.
+        * b) Wait for completion to be signalled from isr.
+        * c) Initialize completion for next iteration.
+        */
+
+       tegra_dc_hold_dc_out(dc);
+       dc->out->user_needs_vblank = true;
+
+       ret = wait_for_completion_interruptible(&dc->out->user_vblank_comp);
+       init_completion(&dc->out->user_vblank_comp);
+       tegra_dc_release_dc_out(dc);
+
+       return ret;
+}
+
 static void tegra_dc_vblank(struct work_struct *work)
 {
        struct tegra_dc *dc = container_of(work, struct tegra_dc, vblank_work);
@@ -1322,6 +1352,13 @@ static void tegra_dc_underflow_handler(struct tegra_dc *dc)
 #ifndef CONFIG_TEGRA_FPGA_PLATFORM
 static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
 {
+       /* pending user vblank, so wakeup */
+       if ((status & (V_BLANK_INT | MSF_INT)) &&
+           (dc->out->user_needs_vblank)) {
+               dc->out->user_needs_vblank = false;
+               complete(&dc->out->user_vblank_comp);
+       }
+
        if (status & V_BLANK_INT) {
                /* Sync up windows. */
                tegra_dc_trigger_windows(dc);
@@ -1538,6 +1575,7 @@ static u32 get_syncpt(struct tegra_dc *dc, int idx)
 static int tegra_dc_init(struct tegra_dc *dc)
 {
        int i;
+       int int_enable;
 
        tegra_dc_writel(dc, 0x00000100, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
        if (dc->ndev->id == 0) {
@@ -1579,8 +1617,12 @@ static int tegra_dc_init(struct tegra_dc *dc)
        tegra_dc_writel(dc, 0x00000000, DC_DISP_DISP_MISC_CONTROL);
 #endif
        /* enable interrupts for vblank, frame_end and underflows */
-       tegra_dc_writel(dc, (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT),
-               DC_CMD_INT_ENABLE);
+       int_enable = (FRAME_END_INT | V_BLANK_INT | ALL_UF_INT);
+       /* for panels with one-shot mode enable tearing effect interrupt */
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               int_enable |= MSF_INT;
+
+       tegra_dc_writel(dc, int_enable, DC_CMD_INT_ENABLE);
        tegra_dc_writel(dc, ALL_UF_INT, DC_CMD_INT_MASK);
 
        tegra_dc_writel(dc, 0x00000000, DC_DISP_BORDER_COLOR);
index 2c6f940..0842094 100644 (file)
@@ -427,6 +427,13 @@ static ssize_t cmu_enable_store(struct device *dev,
 
 static DEVICE_ATTR(cmu_enable, S_IRUGO|S_IWUSR, NULL, cmu_enable_store);
 #endif
+static ssize_t smart_panel_show(struct device *device,
+       struct device_attribute *attr, char  *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "1\n");
+}
+
+static DEVICE_ATTR(smart_panel, S_IRUGO, smart_panel_show, NULL);
 
 void tegra_dc_remove_sysfs(struct device *dev)
 {
@@ -451,6 +458,9 @@ void tegra_dc_remove_sysfs(struct device *dev)
 
        if (sd_settings)
                nvsd_remove_sysfs(dev);
+
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               device_remove_file(dev, &dev_attr_smart_panel);
 }
 
 void tegra_dc_create_sysfs(struct device *dev)
@@ -478,6 +488,9 @@ void tegra_dc_create_sysfs(struct device *dev)
        if (sd_settings)
                error |= nvsd_create_sysfs(dev);
 
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               error |= device_create_file(dev, &dev_attr_smart_panel);
+
        if (error)
                dev_err(&ndev->dev, "Failed to create sysfs attributes!\n");
 }
index a05bd5a..247bfa1 100644 (file)
@@ -84,7 +84,9 @@
 #define DSI_LP_OP_WRITE                        0x1
 #define DSI_LP_OP_READ                 0x2
 
+#define DSI_HOST_IDLE_PERIOD           1000
 static atomic_t dsi_syncpt_rst = ATOMIC_INIT(0);
+
 static bool enable_read_debug;
 module_param(enable_read_debug, bool, 0644);
 MODULE_PARM_DESC(enable_read_debug,
@@ -271,6 +273,7 @@ const u32 init_reg_vs1_ext[] = {
 
 static int tegra_dsi_host_suspend(struct tegra_dc *dc);
 static int tegra_dsi_host_resume(struct tegra_dc *dc);
+static void tegra_dc_dsi_idle_work(struct work_struct *work);
 
 inline unsigned long tegra_dsi_readl(struct tegra_dc_dsi_data *dsi, u32 reg)
 {
@@ -685,6 +688,13 @@ static void tegra_dsi_init_sw(struct tegra_dc *dc,
                dsi->info.video_clock_mode = TEGRA_DSI_VIDEO_CLOCK_CONTINUOUS;
        }
 
+       dsi->host_ref = 0;
+       dsi->host_suspended = false;
+       spin_lock_init(&dsi->host_ref_lock);
+       mutex_init(&dsi->host_resume_lock);
+       init_completion(&dc->out->user_vblank_comp);
+       INIT_DELAYED_WORK(&dsi->idle_work, tegra_dc_dsi_idle_work);
+       dsi->idle_delay = msecs_to_jiffies(DSI_HOST_IDLE_PERIOD);
 }
 
 #define SELECT_T_PHY(platform_t_phy_ns, default_phy, clk_ns, hw_inc) ( \
@@ -1586,6 +1596,10 @@ static void tegra_dsi_soft_reset(struct tegra_dc_dsi_data *dsi)
 static void tegra_dsi_stop_dc_stream(struct tegra_dc *dc,
                                        struct tegra_dc_dsi_data *dsi)
 {
+       /* Mask the MSF interrupt. */
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
+               tegra_dc_mask_interrupt(dc, MSF_INT);
+
        tegra_dc_writel(dc, DISP_CTRL_MODE_STOP, DC_CMD_DISPLAY_COMMAND);
        tegra_dc_writel(dc, 0, DC_DISP_DISP_WIN_OPTIONS);
        tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
@@ -1685,6 +1699,9 @@ static void tegra_dsi_start_dc_stream(struct tegra_dc *dc,
                tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL);
                tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG,
                                                DC_CMD_STATE_CONTROL);
+
+               /* Unmask the MSF interrupt. */
+               tegra_dc_unmask_interrupt(dc, MSF_INT);
        } else {
                /* set continuous mode */
                tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY,
@@ -2493,13 +2510,10 @@ static void tegra_dc_dsi_hold_host(struct tegra_dc *dc)
        struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
 
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) {
-               /*
-                * The reference count should never be more than 1.
-                */
-               BUG_ON(tegra_is_clk_enabled(dc->clk) > 1);
-
-               if (dsi->host_suspended)
-                       tegra_dsi_host_resume(dc);
+               spin_lock(&dsi->host_ref_lock);
+               dsi->host_ref++;
+               spin_unlock(&dsi->host_ref_lock);
+               tegra_dsi_host_resume(dc);
 
                /*
                 * Take an extra refrence to count for the clk_disable in
@@ -2511,14 +2525,27 @@ static void tegra_dc_dsi_hold_host(struct tegra_dc *dc)
 
 static void tegra_dc_dsi_release_host(struct tegra_dc *dc)
 {
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+       struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
+       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE) {
                clk_disable_unprepare(dc->clk);
+               spin_lock(&dsi->host_ref_lock);
+               dsi->host_ref--;
+
+               if (!dsi->host_ref &&
+                   (dsi->status.dc_stream == DSI_DC_STREAM_ENABLE))
+                       schedule_delayed_work(&dsi->idle_work, dsi->idle_delay);
+
+               spin_unlock(&dsi->host_ref_lock);
+       }
 }
 
-static void tegra_dc_dsi_idle(struct tegra_dc *dc)
+static void tegra_dc_dsi_idle_work(struct work_struct *work)
 {
-       if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
-               tegra_dsi_host_suspend(dc);
+       struct tegra_dc_dsi_data *dsi = container_of(
+               to_delayed_work(work), struct tegra_dc_dsi_data, idle_work);
+
+       if (dsi->dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_LP_MODE)
+               tegra_dsi_host_suspend(dsi->dc);
 }
 
 int tegra_dsi_write_data(struct tegra_dc *dc,
@@ -3740,6 +3767,8 @@ static int tegra_dsi_host_suspend(struct tegra_dc *dc)
        if (dsi->host_suspended)
                return 0;
 
+       BUG_ON(!tegra_is_clk_enabled(dc->clk));
+       tegra_dc_io_start(dc);
        dsi->host_suspended = true;
 
        tegra_dsi_stop_dc_stream(dc, dsi);
@@ -3751,6 +3780,7 @@ static int tegra_dsi_host_suspend(struct tegra_dc *dc)
 
        tegra_dc_clk_disable(dc);
 
+       tegra_dc_io_end(dc);
        return err;
 }
 
@@ -3760,8 +3790,12 @@ static int tegra_dsi_host_resume(struct tegra_dc *dc)
        int err = 0;
        struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc);
 
-       if (!dsi->host_suspended)
+       mutex_lock(&dsi->host_resume_lock);
+       cancel_delayed_work_sync(&dsi->idle_work);
+       if (!dsi->host_suspended) {
+               mutex_unlock(&dsi->host_resume_lock);
                return 0;
+       }
 
        tegra_dc_clk_enable(dc);
        switch (dsi->info.suspend_aggr) {
@@ -3809,8 +3843,10 @@ static int tegra_dsi_host_resume(struct tegra_dc *dc)
        }
 
        tegra_dsi_start_dc_stream(dc, dsi);
+
        dsi->enabled = true;
        dsi->host_suspended = false;
+       mutex_unlock(&dsi->host_resume_lock);
 fail:
        return err;
 }
@@ -3986,7 +4022,6 @@ struct tegra_dc_out_ops tegra_dc_dsi_ops = {
        .disable = tegra_dc_dsi_disable,
        .hold = tegra_dc_dsi_hold_host,
        .release = tegra_dc_dsi_release_host,
-       .idle = tegra_dc_dsi_idle,
 #ifdef CONFIG_PM
        .suspend = tegra_dc_dsi_suspend,
        .resume = tegra_dc_dsi_resume,
index 973ad9f..ea45d62 100644 (file)
@@ -82,6 +82,11 @@ struct tegra_dc_dsi_data {
        bool ulpm;
        bool enabled;
        bool host_suspended;
+       struct mutex host_resume_lock;
+       struct delayed_work idle_work;
+       unsigned long idle_delay;
+       spinlock_t host_ref_lock;
+       u8 host_ref;
 
        u8 driven_mode;
        u8 controller_index;
index 34a0ad4..6bc84d1 100644 (file)
@@ -332,8 +332,10 @@ static void tegra_fb_imageblit(struct fb_info *info,
 
 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
 {
+       struct tegra_fb_info *tegra_fb = (struct tegra_fb_info *)info->par;
        struct tegra_fb_modedb modedb;
        struct fb_modelist *modelist;
+       struct fb_vblank vblank = {};
        int i;
 
        switch (cmd) {
@@ -376,6 +378,17 @@ static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long
                        return -EFAULT;
                break;
 
+       case FBIOGET_VBLANK:
+               tegra_dc_get_fbvblank(tegra_fb->win->dc, &vblank);
+
+               if (copy_to_user(
+                       (void __user *)arg, &vblank, sizeof(vblank)))
+                       return -EFAULT;
+               break;
+
+       case FBIO_WAITFORVSYNC:
+               return tegra_dc_wait_for_vsync(tegra_fb->win->dc);
+
        default:
                return -ENOTTY;
        }