]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
video: tegra: dc: implement vblank event
authorMichael I. Gold <gold@nvidia.com>
Mon, 30 Jun 2014 08:42:23 +0000 (01:42 -0700)
committerTodd Poynter <tpoynter@nvidia.com>
Fri, 29 Aug 2014 14:47:02 +0000 (07:47 -0700)
Implement TEGRA_DC_EXT_EVENT_VBLANK for user space notification of
vblank events.  Implement TEGRA_DC_EXT_SET_VBLANK to enable/disable
vblank notification per display.

bug 200016696

Change-Id: Idf8d02b71976a32e06fe972cfb69b838183bcf02
Signed-off-by: Michael I. Gold <gold@nvidia.com>
Reviewed-on: http://git-master/r/432658
Reviewed-on: http://git-master/r/455981
(cherry picked from commit 2945dc94788a450ba5c9d09ec0ecc5c4fe2f3528)
Reviewed-on: http://git-master/r/488542
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Wayne Hsu <wahsu@nvidia.com>
Tested-by: Wayne Hsu <wahsu@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Todd Poynter <tpoynter@nvidia.com>
arch/arm/mach-tegra/include/mach/dc.h
arch/arm/mach-tegra/include/mach/tegra_dc_ext.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/dc_priv_defs.h
drivers/video/tegra/dc/ext/control.c
drivers/video/tegra/dc/ext/dev.c
drivers/video/tegra/dc/ext/events.c
drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h
include/video/tegra_dc_ext.h

index 8883e4d2ff68cc1bce80ac2d06a3bdd6ca4c7a96..2f491772d1a1d1a1af1bf3173c602069c47d2de1 100644 (file)
@@ -605,6 +605,7 @@ struct tegra_dc_out {
 /* Errands use the interrupts */
 #define V_BLANK_FLIP           0
 #define V_BLANK_NVSD           1
+#define V_BLANK_USER           2
 
 #define V_PULSE2_FLIP          0
 #define V_PULSE2_NVSD          1
@@ -791,6 +792,9 @@ bool tegra_dc_get_connected(struct tegra_dc *);
 bool tegra_dc_hpd(struct tegra_dc *dc);
 
 
+bool tegra_dc_has_vsync(struct tegra_dc *dc);
+void tegra_dc_vsync_enable(struct tegra_dc *dc);
+void tegra_dc_vsync_disable(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, unsigned windows);
index 8356f9aa30ff09138489c9934423c71cd265e0b6..ebdbd361327d1339ba451dfb435ef383f16262a1 100644 (file)
@@ -37,6 +37,7 @@ int tegra_dc_ext_disable(struct tegra_dc_ext *dc_ext);
 int tegra_dc_ext_restore(struct tegra_dc_ext *dc_ext);
 
 int tegra_dc_ext_process_hotplug(int output);
+int tegra_dc_ext_process_vblank(int output, ktime_t timestamp);
 int tegra_dc_ext_process_bandwidth_renegotiate(int output,
                                        struct tegra_dc_bw_data *bw);
 
@@ -81,6 +82,11 @@ int tegra_dc_ext_process_hotplug(int output)
 {
        return 0;
 }
+static inline
+int tegra_dc_ext_process_vblank(int output, ktime_t timestamp)
+{
+       return 0;
+}
 #endif /* CONFIG_TEGRA_DC_EXTENSIONS */
 
 #endif /* __MACH_TEGRA_DC_EXT_H */
index c2ecd49c6cc4bf801f29ef425ccb3e0c3faa859e..9304f304e06301d3a92ff1e5fae8e2f96981fe02 100644 (file)
@@ -479,6 +479,25 @@ void tegra_dc_put(struct tegra_dc *dc)
        tegra_dc_io_end(dc);
 }
 
+void tegra_dc_hold_dc_out(struct tegra_dc *dc)
+{
+       if (1 == atomic_inc_return(&dc->holding)) {
+               tegra_dc_get(dc);
+               if (dc->out_ops && dc->out_ops->hold)
+                       dc->out_ops->hold(dc);
+               atomic_inc(&dc->holding);
+       }
+}
+
+void tegra_dc_release_dc_out(struct tegra_dc *dc)
+{
+       if (0 == atomic_dec_return(&dc->holding)) {
+               if (dc->out_ops && dc->out_ops->release)
+                       dc->out_ops->release(dc);
+               tegra_dc_put(dc);
+       }
+}
+
 #define DUMP_REG(a) do {                       \
        snprintf(buff, sizeof(buff), "%-32s\t%03x\t%08lx\n",  \
                 #a, a, tegra_dc_readl(dc, a));               \
@@ -1573,6 +1592,23 @@ int tegra_dc_wait_for_vsync(struct tegra_dc *dc)
        return ret;
 }
 
+void tegra_dc_vsync_enable(struct tegra_dc *dc)
+{
+       mutex_lock(&dc->lock);
+       set_bit(V_BLANK_USER, &dc->vblank_ref_count);
+       tegra_dc_unmask_interrupt(dc, V_BLANK_INT);
+       mutex_unlock(&dc->lock);
+}
+
+void tegra_dc_vsync_disable(struct tegra_dc *dc)
+{
+       mutex_lock(&dc->lock);
+       clear_bit(V_BLANK_USER, &dc->vblank_ref_count);
+       if (!dc->vblank_ref_count)
+               tegra_dc_mask_interrupt(dc, V_BLANK_INT);
+       mutex_unlock(&dc->lock);
+}
+
 static void tegra_dc_prism_update_backlight(struct tegra_dc *dc)
 {
        /* Do the actual brightness update outside of the mutex dc->lock */
@@ -1789,13 +1825,22 @@ static void tegra_dc_vpulse2(struct work_struct *work)
 }
 #endif
 
-static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
+static void tegra_dc_process_vblank(struct tegra_dc *dc, ktime_t timestamp)
+{
+       if (test_bit(V_BLANK_USER, &dc->vblank_ref_count))
+               tegra_dc_ext_process_vblank(dc->ndev->id, timestamp);
+}
+
+static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status,
+                               ktime_t timestamp)
 {
        /* 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 | MSF_INT)) {
+               if (dc->out->user_needs_vblank) {
+                       dc->out->user_needs_vblank = false;
+                       complete(&dc->out->user_vblank_comp);
+               }
+               tegra_dc_process_vblank(dc, timestamp);
        }
 
        if (status & V_BLANK_INT) {
@@ -1824,12 +1869,16 @@ static void tegra_dc_one_shot_irq(struct tegra_dc *dc, unsigned long status)
 #endif
 }
 
-static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status)
+static void tegra_dc_continuous_irq(struct tegra_dc *dc, unsigned long status,
+                               ktime_t timestamp)
 {
        /* Schedule any additional bottom-half vblank actvities. */
        if (status & V_BLANK_INT)
                queue_work(system_freezable_wq, &dc->vblank_work);
 
+       if (status & (V_BLANK_INT | MSF_INT))
+               tegra_dc_process_vblank(dc, timestamp);
+
        if (status & FRAME_END_INT) {
                struct timespec tm = CURRENT_TIME;
                dc->frame_end_timestamp = timespec_to_ns(&tm);
@@ -1868,6 +1917,7 @@ bool tegra_dc_does_vsync_separate(struct tegra_dc *dc, s64 new_ts, s64 old_ts)
 
 static irqreturn_t tegra_dc_irq(int irq, void *ptr)
 {
+       ktime_t timestamp = ktime_get();
        struct tegra_dc *dc = ptr;
        unsigned long status;
        unsigned long underflow_mask;
@@ -1916,9 +1966,9 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
        }
 
        if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
-               tegra_dc_one_shot_irq(dc, status);
+               tegra_dc_one_shot_irq(dc, status, timestamp);
        else
-               tegra_dc_continuous_irq(dc, status);
+               tegra_dc_continuous_irq(dc, status, timestamp);
 
        if (dc->nvsr)
                tegra_dc_nvsr_irq(dc->nvsr, status);
@@ -2437,6 +2487,12 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
 
        tegra_dc_get(dc);
 
+       if (atomic_read(&dc->holding)) {
+               /* Force release all refs but the last one */
+               atomic_set(&dc->holding, 1);
+               tegra_dc_release_dc_out(dc);
+       }
+
        if (dc->out && dc->out->prepoweroff)
                dc->out->prepoweroff();
 
index ccdae2bc47ca67760f4542040baf15876fd33750..b3418873095a42eb8859231b4c35c1a42b1b0b8b 100644 (file)
@@ -232,6 +232,7 @@ struct tegra_dc {
        atomic_t                        frame_end_ref;
 
        bool                            mode_dirty;
+       atomic_t                        holding;
 
        u32                             reserved_bw;
        u32                             available_bw;
index d0d0f100095d07ee5ab8bf3ad2250c2c0b3aab75..7d9391e01a979f7d3795a8cc64c9cede7b3915bd 100644 (file)
@@ -47,6 +47,11 @@ int tegra_dc_ext_process_hotplug(int output)
        return tegra_dc_ext_queue_hotplug(&g_control, output);
 }
 
+int tegra_dc_ext_process_vblank(int output, ktime_t timestamp)
+{
+       return tegra_dc_ext_queue_vblank(&g_control, output, timestamp);
+}
+
 static int
 get_output_properties(struct tegra_dc_ext_control_output_properties *properties)
 {
index 66386cba06eea3560e721eb2edd3c55a4fb8d43b..0483bf6e62fb2e63688c348263487a30ecf0ee5d 100644 (file)
@@ -113,6 +113,7 @@ struct tegra_dc_ext_flip_data {
 };
 
 static void tegra_dc_ext_unpin_window(struct tegra_dc_ext_win *win);
+static int tegra_dc_ext_set_vblank(struct tegra_dc_ext *ext, bool enable);
 
 static inline s64 tegra_timespec_to_ns(const struct tegra_timespec *ts)
 {
@@ -234,6 +235,11 @@ int tegra_dc_ext_disable(struct tegra_dc_ext *ext)
 
        set_enable(ext, false);
 
+       /*
+        * Disable vblank requests
+        */
+       tegra_dc_ext_set_vblank(ext, false);
+
        /*
         * Flush the flip queue -- note that this must be called with dc->lock
         * unlocked or else it will hang.
@@ -462,6 +468,27 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
        return err;
 }
 
+static int tegra_dc_ext_set_vblank(struct tegra_dc_ext *ext, bool enable)
+{
+       struct tegra_dc *dc;
+
+       if (ext->vblank_enabled == enable)
+               return 0;
+
+       dc = ext->dc;
+
+       if (enable) {
+               tegra_dc_hold_dc_out(dc);
+               tegra_dc_vsync_enable(dc);
+       } else {
+               tegra_dc_vsync_disable(dc);
+               tegra_dc_release_dc_out(dc);
+       }
+
+       ext->vblank_enabled = enable;
+       return 0;
+}
+
 static void (*flip_callback)(void);
 static spinlock_t flip_callback_lock;
 static bool init_tegra_dc_flip_callback_called;
@@ -1630,6 +1657,16 @@ static long tegra_dc_ioctl(struct file *filp, unsigned int cmd,
 #endif
        }
 
+       case TEGRA_DC_EXT_SET_VBLANK:
+       {
+               struct tegra_dc_ext_set_vblank args;
+
+               if (copy_from_user(&args, user_arg, sizeof(args)))
+                       return -EFAULT;
+
+               return tegra_dc_ext_set_vblank(user->ext, args.enable);
+       }
+
        default:
                return -EINVAL;
        }
index e16a00963e697d5968bd7276d451ef923b2e0512..123eda72d14585f6eac4e6541b8984f827b32a5b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * drivers/video/tegra/dc/ext/events.c
  *
- * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved.
+ * Copyright (c) 2011-2014, NVIDIA CORPORATION, All rights reserved.
  *
  * Author: Robert Morell <rmorell@nvidia.com>
  *
@@ -141,6 +141,8 @@ static int tegra_dc_ext_queue_event(struct tegra_dc_ext_control *control,
        struct list_head *cur;
        int retval = 0;
 
+       WARN_ON(event->data_size > TEGRA_DC_EXT_EVENT_MAX_SZ);
+
        mutex_lock(&control->lock);
        list_for_each(cur, &control->users) {
                struct tegra_dc_ext_control_user *user;
@@ -196,6 +198,26 @@ int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *control, int output)
        return 0;
 }
 
+int tegra_dc_ext_queue_vblank(struct tegra_dc_ext_control *control, int output,
+                               ktime_t timestamp)
+{
+       struct {
+               struct tegra_dc_ext_event event;
+               struct tegra_dc_ext_control_event_vblank vblank;
+       } __packed pack;
+
+       pack.event.type = TEGRA_DC_EXT_EVENT_VBLANK;
+       pack.event.data_size = sizeof(pack.vblank);
+
+       pack.vblank.handle = output;
+       pack.vblank.reserved = 0;
+       pack.vblank.timestamp_ns = ktime_to_ns(timestamp);
+
+       tegra_dc_ext_queue_event(control, &pack.event);
+
+       return 0;
+}
+
 int tegra_dc_ext_queue_bandwidth_renegotiate(
                struct tegra_dc_ext_control *control, int output,
                struct tegra_dc_bw_data *data)
index f23e18828c1dde27f9fda59c19aeca09874dd447..a9fafde45dc2eccbeb23e14fd3b318fcc4407dbe 100644 (file)
@@ -86,13 +86,16 @@ struct tegra_dc_ext {
        } cursor;
 
        bool                            enabled;
+       bool                            vblank_enabled;
 };
 
-#define TEGRA_DC_EXT_EVENT_MASK_ALL \
-       (TEGRA_DC_EXT_EVENT_HOTPLUG | TEGRA_DC_EXT_EVENT_BANDWIDTH_INC | \
+#define TEGRA_DC_EXT_EVENT_MASK_ALL            \
+       (TEGRA_DC_EXT_EVENT_HOTPLUG |           \
+        TEGRA_DC_EXT_EVENT_VBLANK |            \
+        TEGRA_DC_EXT_EVENT_BANDWIDTH_INC |     \
         TEGRA_DC_EXT_EVENT_BANDWIDTH_DEC)
 
-#define TEGRA_DC_EXT_EVENT_MAX_SZ      8
+#define TEGRA_DC_EXT_EVENT_MAX_SZ      16
 
 struct tegra_dc_ext_event_list {
        struct tegra_dc_ext_event       event;
@@ -154,6 +157,8 @@ extern int tegra_dc_ext_control_init(void);
 
 extern int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *,
                                      int output);
+extern int tegra_dc_ext_queue_vblank(struct tegra_dc_ext_control *,
+                                     int output, ktime_t timestamp);
 extern int tegra_dc_ext_queue_bandwidth_renegotiate(
                        struct tegra_dc_ext_control *, int output,
                        struct tegra_dc_bw_data *data);
index 78e344d5ad6f73c92d694a51dfda87e13baec9db..3f5f66084bffc7e6b31f5e5ae8becc9dbf116b05 100644 (file)
@@ -186,6 +186,15 @@ struct tegra_dc_ext_flip_3 {
        __u16 dirty_rect[4]; /* x,y,w,h for partial screen update. 0 ignores */
 };
 
+/*
+ * vblank control - enable or disable vblank events
+ */
+
+struct tegra_dc_ext_set_vblank {
+       __u8    enable;
+       __u8    reserved[3]; /* unused - must be 0 */
+};
+
 /*
  * Cursor image format:
  *
@@ -433,6 +442,9 @@ struct tegra_dc_ext_feature {
 #define TEGRA_DC_EXT_FLIP3 \
        _IOWR('D', 0x14, struct tegra_dc_ext_flip_3)
 
+#define TEGRA_DC_EXT_SET_VBLANK \
+       _IOW('D', 0x15, struct tegra_dc_ext_set_vblank)
+
 enum tegra_dc_ext_control_output_type {
        TEGRA_DC_EXT_DSI,
        TEGRA_DC_EXT_LVDS,
@@ -483,13 +495,21 @@ struct tegra_dc_ext_event {
        char    data[0];
 };
 
-#define TEGRA_DC_EXT_EVENT_HOTPLUG     0x1
+/* Events types are bits in a mask */
+#define TEGRA_DC_EXT_EVENT_HOTPLUG                     (1 << 0)
 struct tegra_dc_ext_control_event_hotplug {
        __u32 handle;
 };
 
-#define TEGRA_DC_EXT_EVENT_BANDWIDTH_INC       0x3
-#define TEGRA_DC_EXT_EVENT_BANDWIDTH_DEC       0x4
+#define TEGRA_DC_EXT_EVENT_VBLANK                      (1 << 1)
+struct tegra_dc_ext_control_event_vblank {
+       __u32 handle;
+       __u32 reserved; /* unused */
+       __u64 timestamp_ns;
+};
+
+#define TEGRA_DC_EXT_EVENT_BANDWIDTH_INC       (1 << 2)
+#define TEGRA_DC_EXT_EVENT_BANDWIDTH_DEC       (1 << 3)
 struct tegra_dc_ext_control_event_bandwidth {
        __u32 handle;
        __u32 total_bw;