]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
video: tegra: host: podgov: Don't turn on the dev
authorArto Merilainen <amerilainen@nvidia.com>
Wed, 13 Nov 2013 09:20:23 +0000 (11:20 +0200)
committerArto Merilainen <amerilainen@nvidia.com>
Fri, 15 Nov 2013 11:09:28 +0000 (13:09 +0200)
This far we have turned on the device in all code paths that
may lead to scaling. In most cases this is correct, however,
near suspend we may end up powering up the device unnecessarily.

This patch modifies podgov to not turn on device if it is not
already powered. If the device is powered, we take a reference to
ensure that the device stays on over the operations.

In addition, this patch revisits all cancel(_delayed)_work(_sync)
users to make sure that the workers are actually finished.

Bug 1404859

Change-Id: I40608420d0d87aea5aa73fa601b5c202025923e2
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
drivers/video/tegra/host/gr3d/pod_scaling.c

index 23da823ac5f831ae4be6caaf21efa46d1a56c761..8a7be2dd0fb94b099daf5bbac1ab9237b3772f93 100644 (file)
@@ -155,6 +155,16 @@ enum podgov_adjustment_type {
 };
 
 
+static void stop_podgov_workers(struct podgov_info_rec *podgov)
+{
+       /* idle_timer can rearm itself */
+       do {
+               cancel_delayed_work_sync(&podgov->idle_timer);
+       } while (delayed_work_pending(&podgov->idle_timer));
+
+       cancel_work_sync(&podgov->work);
+}
+
 /*******************************************************************************
  * scaling_limit(df, freq)
  *
@@ -185,7 +195,12 @@ static void podgov_clocks_handler(struct work_struct *work)
        unsigned long freq;
 
        /* make sure the device is alive before doing any scaling */
-       nvhost_module_busy(pdev);
+       nvhost_module_busy_noresume(pdev);
+       if (!pm_runtime_active(&pdev->dev)) {
+               nvhost_module_idle(pdev);
+               return;
+       }
+
        mutex_lock(&df->lock);
 
        if (!podgov->enable)
@@ -232,10 +247,9 @@ void nvhost_scale3d_suspend(struct device *dev)
                mutex_unlock(&df->lock);
                return;
        }
-       cancel_delayed_work(&podgov->idle_timer);
        mutex_unlock(&df->lock);
 
-       cancel_work_sync(&podgov->work);
+       stop_podgov_workers(podgov);
 }
 
 /*******************************************************************************
@@ -277,35 +291,47 @@ static void podgov_enable(struct device *dev, int enable)
        struct nvhost_device_data *pdata = platform_get_drvdata(d);
        struct devfreq *df = pdata->power_manager;
        struct podgov_info_rec *podgov;
-       bool cancel = false;
 
        if (!df)
                return;
 
        /* make sure the device is alive before doing any scaling */
-       nvhost_module_busy(d);
+       nvhost_module_busy_noresume(d);
+
        mutex_lock(&df->lock);
 
        podgov = df->data;
 
        trace_podgov_enabled(enable);
 
-       if (enable && df->min_freq != df->max_freq) {
-               podgov->enable = 1;
-       } else {
-               cancel = true;
-               cancel_delayed_work(&podgov->idle_timer);
-               podgov->enable = 0;
-               podgov->adjustment_frequency = df->max_freq;
-               podgov->adjustment_type = ADJUSTMENT_LOCAL;
-               update_devfreq(df);
-       }
+       /* bad configuration. quit. */
+       if (df->min_freq == df->max_freq)
+               goto exit_unlock;
+
+       /* store the enable information */
+       podgov->enable = enable;
+
+       /* skip local adjustment if we are enabling or the device is
+        * suspended */
+       if (enable || !pm_runtime_active(&d->dev))
+               goto exit_unlock;
+
+       /* full speed */
+       podgov->adjustment_frequency = df->max_freq;
+       podgov->adjustment_type = ADJUSTMENT_LOCAL;
+       update_devfreq(df);
 
        mutex_unlock(&df->lock);
+
        nvhost_module_idle(d);
 
-       if (cancel)
-               cancel_work_sync(&podgov->work);
+       stop_podgov_workers(podgov);
+
+       return;
+
+exit_unlock:
+       mutex_unlock(&df->lock);
+       nvhost_module_idle(d);
 }
 
 /*******************************************************************************
@@ -347,33 +373,44 @@ static void podgov_set_user_ctl(struct device *dev, int user)
        struct nvhost_device_data *pdata = platform_get_drvdata(d);
        struct devfreq *df = pdata->power_manager;
        struct podgov_info_rec *podgov;
-       int cancel = 0;
+       int old_user;
 
        if (!df)
                return;
 
-       nvhost_module_busy(d);
+       /* make sure the device is alive before doing any scaling */
+       nvhost_module_busy_noresume(d);
+
        mutex_lock(&df->lock);
        podgov = df->data;
 
        trace_podgov_set_user_ctl(user);
 
-       if (podgov->enable && user && !podgov->p_user) {
-               cancel = 1;
-               cancel_delayed_work(&podgov->idle_timer);
+       /* store the new user value */
+       old_user = podgov->p_user;
+       podgov->p_user = user;
 
-               podgov->adjustment_frequency = podgov->p_freq_request;
-               podgov->adjustment_type = ADJUSTMENT_LOCAL;
-               update_devfreq(df);
-       }
+       /* skip scaling, if scaling (or the whole device) is turned off
+        * - or the scaling already was in user mode */
+       if (!pm_runtime_active(&d->dev) || !podgov->enable ||
+           !(user && !old_user))
+               goto exit_unlock;
 
-       podgov->p_user = user;
+       /* write request */
+       podgov->adjustment_frequency = podgov->p_freq_request;
+       podgov->adjustment_type = ADJUSTMENT_LOCAL;
+       update_devfreq(df);
 
        mutex_unlock(&df->lock);
        nvhost_module_idle(d);
 
-       if (cancel)
-               cancel_work_sync(&podgov->work);
+       stop_podgov_workers(podgov);
+
+       return;
+
+exit_unlock:
+       mutex_unlock(&df->lock);
+       nvhost_module_idle(d);
 }
 
 /*******************************************************************************
@@ -418,7 +455,9 @@ static void podgov_set_freq_request(struct device *dev, int freq_request)
        if (!df)
                return;
 
-       nvhost_module_busy(d);
+       /* make sure the device is alive before doing any scaling */
+       nvhost_module_busy_noresume(d);
+
        mutex_lock(&df->lock);
 
        podgov = df->data;
@@ -427,8 +466,10 @@ static void podgov_set_freq_request(struct device *dev, int freq_request)
 
        podgov->p_freq_request = freq_request;
 
-       if (podgov->enable && podgov->p_user) {
-
+       /* update the request only if podgov is enabled, device is turned on
+        * and the scaling is in user mode */
+       if (podgov->enable && podgov->p_user &&
+           pm_runtime_active(&d->dev)) {
                podgov->adjustment_frequency = freq_request;
                podgov->adjustment_type = ADJUSTMENT_LOCAL;
                update_devfreq(df);
@@ -699,7 +740,8 @@ static void podgov_idle_handler(struct work_struct *work)
        }
 
        if (podgov->last_event_type == DEVICE_IDLE &&
-               df->previous_freq > df->min_freq)
+           df->previous_freq > df->min_freq &&
+           podgov->p_user == false)
                notify_idle = 1;
 
        mutex_unlock(&df->lock);
@@ -738,7 +780,12 @@ static int nvhost_scale3d_set_throughput_hint(struct notifier_block *nb,
        pdev = to_platform_device(df->dev.parent);
 
        /* make sure the device is alive before doing any scaling */
-       nvhost_module_busy(pdev);
+       nvhost_module_busy_noresume(pdev);
+       if (!pm_runtime_active(&pdev->dev)) {
+               nvhost_module_idle(pdev);
+               return 0;
+       }
+
        mutex_lock(&podgov->power_manager->lock);
 
        podgov->block--;