video: tegra: Prevent hang when output disabled
Robert Morell [Fri, 4 Mar 2011 04:54:09 +0000 (20:54 -0800)]
This adds code to track when the dc is disabled and prevent flips or
cursor moves.  This prevents system hangs since the dc is powergated
when it's disabled.

bug 818525

Original-Change-Id: I061da1f6a831fa14a216520e603e0fbc5dbb0437
Signed-off-by: Robert Morell <rmorell@nvidia.com>
Reviewed-on: http://git-master/r/40519
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>

Rebase-Id: Rb648ef48bd3528344cf090c49093dcb258c20150

arch/arm/mach-tegra/include/mach/tegra_dc_ext.h
drivers/video/tegra/dc/dc.c
drivers/video/tegra/dc/ext/cursor.c
drivers/video/tegra/dc/ext/dev.c
drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h

index 7156695..c942745 100644 (file)
@@ -31,8 +31,9 @@ struct tegra_dc_ext *tegra_dc_ext_register(struct nvhost_device *ndev,
                                           struct tegra_dc *dc);
 void tegra_dc_ext_unregister(struct tegra_dc_ext *dc_ext);
 
-/* called by display controller on suspend */
-void tegra_dc_ext_suspend(struct tegra_dc_ext *dc_ext);
+/* called by display controller on enable/disable */
+void tegra_dc_ext_enable(struct tegra_dc_ext *dc_ext);
+void tegra_dc_ext_disable(struct tegra_dc_ext *dc_ext);
 
 #else /* CONFIG_TEGRA_DC_EXTENSIONS */
 
@@ -57,7 +58,11 @@ void tegra_dc_ext_unregister(struct tegra_dc_ext *dc_ext)
 {
 }
 static inline
-void tegra_dc_ext_suspend(struct tegra_dc_ext *dc_ext)
+void tegra_dc_ext_enable(struct tegra_dc_ext *dc_ext)
+{
+}
+static inline
+void tegra_dc_ext_disable(struct tegra_dc_ext *dc_ext)
 {
 }
 #endif /* CONFIG_TEGRA_DC_EXTENSIONS */
index 3683474..976770c 100644 (file)
@@ -2047,6 +2047,8 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
        /* force a full blending update */
        dc->blend.z[0] = -1;
 
+       tegra_dc_ext_enable(dc->ext);
+
        return true;
 }
 
@@ -2204,6 +2206,8 @@ void tegra_dc_disable(struct tegra_dc *dc)
        if (dc->overlay)
                tegra_overlay_disable(dc->overlay);
 
+       tegra_dc_ext_disable(dc->ext);
+
        mutex_lock(&dc->lock);
 
        if (dc->enabled) {
@@ -2226,6 +2230,8 @@ static void tegra_dc_reset_worker(struct work_struct *work)
 
        dev_warn(&dc->ndev->dev, "overlay stuck in underflow state.  resetting.\n");
 
+       tegra_dc_ext_disable(dc->ext);
+
        mutex_lock(&shared_lock);
        mutex_lock(&dc->lock);
 
@@ -2385,6 +2391,12 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
        else
                dev_err(&ndev->dev, "No default output specified.  Leaving output disabled.\n");
 
+       dc->ext = tegra_dc_ext_register(ndev, dc);
+       if (IS_ERR_OR_NULL(dc->ext)) {
+               dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n");
+               dc->ext = NULL;
+       }
+
        mutex_lock(&dc->lock);
        if (dc->enabled)
                _tegra_dc_enable(dc);
@@ -2420,12 +2432,6 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
        if (dc->out && dc->out->hotplug_init)
                dc->out->hotplug_init();
 
-       dc->ext = tegra_dc_ext_register(ndev, dc);
-       if (IS_ERR_OR_NULL(dc->ext)) {
-               dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n");
-               dc->ext = NULL;
-       }
-
        if (dc->out_ops && dc->out_ops->detect)
                dc->out_ops->detect(dc);
 
@@ -2468,6 +2474,8 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
                        release_resource(dc->fb_mem);
        }
 
+       tegra_dc_ext_disable(dc->ext);
+
        if (dc->ext)
                tegra_dc_ext_unregister(dc->ext);
 
@@ -2495,13 +2503,14 @@ static int tegra_dc_suspend(struct nvhost_device *ndev, pm_message_t state)
        if (dc->overlay)
                tegra_overlay_disable(dc->overlay);
 
+       tegra_dc_ext_disable(dc->ext);
+
        mutex_lock(&dc->lock);
 
        if (dc->out_ops && dc->out_ops->suspend)
                dc->out_ops->suspend(dc);
 
        if (dc->enabled) {
-               tegra_dc_ext_suspend(dc->ext);
                _tegra_dc_disable(dc);
 
                dc->suspended = true;
index e25ca0f..d8fa5fd 100644 (file)
@@ -105,17 +105,20 @@ int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
        mutex_lock(&ext->cursor.lock);
 
        if (ext->cursor.user != user) {
-               mutex_unlock(&ext->cursor.lock);
-               return -EACCES;
+               ret = -EACCES;
+               goto unlock;
+       }
+
+       if (!ext->enabled) {
+               ret = -ENXIO;
+               goto unlock;
        }
 
        old_handle = ext->cursor.cur_handle;
 
        ret = tegra_dc_ext_pin_window(user, args->buff_id, &handle, &phys_addr);
-       if (ret) {
-               mutex_unlock(&ext->cursor.lock);
-               return -EACCES;
-       }
+       if (ret)
+               goto unlock;
 
        ext->cursor.cur_handle = handle;
 
@@ -138,6 +141,11 @@ int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user,
        }
 
        return 0;
+
+unlock:
+       mutex_unlock(&ext->cursor.lock);
+
+       return ret;
 }
 
 int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
@@ -147,12 +155,18 @@ int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
        struct tegra_dc *dc = ext->dc;
        u32 win_options;
        bool enable;
+       int ret;
 
        mutex_lock(&ext->cursor.lock);
 
        if (ext->cursor.user != user) {
-               mutex_unlock(&ext->cursor.lock);
-               return -EACCES;
+               ret = -EACCES;
+               goto unlock;
+       }
+
+       if (!ext->enabled) {
+               ret = -ENXIO;
+               goto unlock;
        }
 
        enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE);
@@ -181,4 +195,9 @@ int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user,
        mutex_unlock(&ext->cursor.lock);
 
        return 0;
+
+unlock:
+       mutex_unlock(&ext->cursor.lock);
+
+       return ret;
 }
index b51b937..7e07773 100644 (file)
@@ -124,10 +124,39 @@ static int tegra_dc_ext_put_window(struct tegra_dc_ext_user *user,
        return ret;
 }
 
-void tegra_dc_ext_suspend(struct tegra_dc_ext *ext)
+static void set_enable(struct tegra_dc_ext *ext, bool en)
 {
        int i;
 
+       /*
+        * Take all locks to make sure any flip requests or cursor moves are
+        * out of their critical sections
+        */
+       for (i = 0; i < ext->dc->n_windows; i++)
+               mutex_lock(&ext->win[i].lock);
+       mutex_lock(&ext->cursor.lock);
+
+       ext->enabled = en;
+
+       mutex_unlock(&ext->cursor.lock);
+       for (i = ext->dc->n_windows - 1; i >= 0 ; i--)
+               mutex_unlock(&ext->win[i].lock);
+}
+
+void tegra_dc_ext_enable(struct tegra_dc_ext *ext)
+{
+       set_enable(ext, true);
+}
+
+void tegra_dc_ext_disable(struct tegra_dc_ext *ext)
+{
+       int i;
+       set_enable(ext, false);
+
+       /*
+        * Flush the flip queue -- note that this must be called with dc->lock
+        * unlocked or else it will hang.
+        */
        for (i = 0; i < ext->dc->n_windows; i++) {
                struct tegra_dc_ext_win *win = &ext->win[i];
 
@@ -357,6 +386,11 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
        if (ret)
                goto fail_pin;
 
+       if (!ext->enabled) {
+               ret = -ENXIO;
+               goto unlock;
+       }
+
        for (i = 0; i < DC_N_WINDOWS; i++) {
                u32 syncpt_max;
                int index = args->win[i].index;
@@ -382,6 +416,9 @@ static int tegra_dc_ext_flip(struct tegra_dc_ext_user *user,
 
        return 0;
 
+unlock:
+       unlock_windows_for_flip(user, args);
+
 fail_pin:
        while (i--) {
                if (!data->win[i].handle)
index 3040cf7..251c072 100644 (file)
@@ -63,6 +63,8 @@ struct tegra_dc_ext {
                struct nvmap_handle_ref         *cur_handle;
                struct mutex                    lock;
        } cursor;
+
+       bool                            enabled;
 };
 
 extern int tegra_dc_ext_pin_window(struct tegra_dc_ext_user *user, u32 id,