misc: tegra-throughput: Use notifiers
Arto Merilainen [Wed, 25 Sep 2013 12:39:34 +0000 (15:39 +0300)]
We used to deliver this information by calling directly a function
inside nvhost_pod. This is not suitable for our needs as we want
to be able to use the same governor with several different (graphics)
devices in the same system.

This patch modifies tegra-throughput to use notifier chain for
delivering information about changed throughput hint.

Bug 1364804

Change-Id: Ifce1c84ae68cdcf54b00402efa80aee0b220bc9e
Signed-off-by: Arto Merilainen <amerilainen@nvidia.com>
(cherry picked from commit 9f10cf7082055f81c201ab5f5574c2f8fe8c6795)
Signed-off-by: Ajay Nandakumar <anandakumarm@nvidia.com>

drivers/misc/tegra-throughput.c
drivers/video/tegra/host/gr3d/pod_scaling.c
include/linux/nvhost.h
include/linux/tegra-throughput.h [new file with mode: 0644]

index 49b5093..968371b 100644 (file)
@@ -27,6 +27,8 @@
 #include <linux/throughput_ioctl.h>
 #include <linux/module.h>
 #include <linux/nvhost.h>
+#include <linux/notifier.h>
+#include <linux/tegra-throughput.h>
 #include <mach/dc.h>
 
 #define DEFAULT_SYNC_RATE 60000 /* 60 Hz */
@@ -46,11 +48,21 @@ static int throughput_hint;
 static int sync_rate;
 static int throughput_active_app_count;
 
+BLOCKING_NOTIFIER_HEAD(throughput_notifier_list);
+EXPORT_SYMBOL(throughput_notifier_list);
+
 static void set_throughput_hint(struct work_struct *work)
 {
        /* notify throughput hint clients here */
-       nvhost_scale3d_set_throughput_hint(throughput_hint);
+       blocking_notifier_call_chain(&throughput_notifier_list,
+                                    throughput_hint, NULL);
+}
+
+int tegra_throughput_get_hint(void)
+{
+       return throughput_hint;
 }
+EXPORT_SYMBOL(tegra_throughput_get_hint);
 
 static void throughput_flip_callback(void)
 {
index 86328ce..72f11cf 100644 (file)
@@ -41,6 +41,9 @@
 #include <linux/clk/tegra.h>
 #include <linux/tegra-soc.h>
 
+#include <linux/notifier.h>
+#include <linux/tegra-throughput.h>
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/nvhost_podgov.h>
 
@@ -127,6 +130,7 @@ struct podgov_info_rec {
        unsigned int            hint_avg;
        int                     block;
 
+       struct notifier_block   throughput_hint_notifier;
 };
 
 /*******************************************************************************
@@ -145,10 +149,6 @@ enum podgov_adjustment_type {
 };
 
 
-/* Some functions cannot get pointer to podgov anywhere :-(
- * Yes, this should be fixed */
-struct podgov_info_rec *local_podgov;
-
 /*******************************************************************************
  * scaling_limit(df, freq)
  *
@@ -693,21 +693,25 @@ static void podgov_idle_handler(struct work_struct *work)
  * required throughput
  ******************************************************************************/
 
-void nvhost_scale3d_set_throughput_hint(int hint)
+static int nvhost_scale3d_set_throughput_hint(struct notifier_block *nb,
+                                             unsigned long action, void *data)
 {
-       struct podgov_info_rec *podgov = local_podgov;
+       struct podgov_info_rec *podgov =
+               container_of(nb, struct podgov_info_rec,
+                            throughput_hint_notifier);
        struct devfreq *df;
 
+       int hint = tegra_throughput_get_hint();
        long idle;
        long curr, target;
        int avg_idle, avg_hint, scale_score;
        unsigned int smooth;
 
        if (!podgov)
-               return;
+               return NOTIFY_DONE;
        df = podgov->power_manager;
        if (!df)
-               return;
+               return NOTIFY_DONE;
 
        mutex_lock(&podgov->power_manager->lock);
 
@@ -717,7 +721,7 @@ void nvhost_scale3d_set_throughput_hint(int hint)
                !podgov->p_use_throughput_hint ||
                podgov->block > 0) {
                mutex_unlock(&podgov->power_manager->lock);
-               return;
+               return NOTIFY_DONE;
        }
 
        trace_podgov_hint(podgov->idle_estimate, hint);
@@ -760,8 +764,8 @@ void nvhost_scale3d_set_throughput_hint(int hint)
                avg_hint);
 
        mutex_unlock(&podgov->power_manager->lock);
+       return NOTIFY_OK;
 }
-EXPORT_SYMBOL(nvhost_scale3d_set_throughput_hint);
 
 /*******************************************************************************
  * debugfs interface for controlling 3d clock scaling on the fly
@@ -1050,10 +1054,6 @@ static int nvhost_pod_init(struct devfreq *df)
                goto err_alloc_podgov;
        df->data = (void *)podgov;
 
-       /* This should be removed after the governor include also the hint
-        * interface */
-       local_podgov = podgov;
-
        /* Initialise workers */
        INIT_WORK(&podgov->work, podgov_clocks_handler);
        INIT_DELAYED_WORK(&podgov->idle_timer, podgov_idle_handler);
@@ -1151,6 +1151,12 @@ static int nvhost_pod_init(struct devfreq *df)
 
        nvhost_scale3d_debug_init(df);
 
+       /* register the governor to throughput hint notifier chain */
+       podgov->throughput_hint_notifier.notifier_call =
+               &nvhost_scale3d_set_throughput_hint;
+       blocking_notifier_chain_register(&throughput_notifier_list,
+                                        &podgov->throughput_hint_notifier);
+
        return 0;
 
 err_get_freqs:
@@ -1177,6 +1183,8 @@ static void nvhost_pod_exit(struct devfreq *df)
        struct podgov_info_rec *podgov = df->data;
        struct platform_device *d = to_platform_device(df->dev.parent);
 
+       blocking_notifier_chain_unregister(&throughput_notifier_list,
+                                          &podgov->throughput_hint_notifier);
        cancel_work_sync(&podgov->work);
        cancel_delayed_work(&podgov->idle_timer);
 
@@ -1187,7 +1195,6 @@ static void nvhost_pod_exit(struct devfreq *df)
        nvhost_scale3d_debug_deinit(df);
 
        kfree(podgov);
-       local_podgov = NULL;
 }
 
 static int nvhost_pod_event_handler(struct devfreq *df,
index af09ad9..df3bf93 100644 (file)
@@ -313,8 +313,6 @@ u32 nvhost_syncpt_read_ext(struct platform_device *dev, u32 id);
 int nvhost_syncpt_wait_timeout_ext(struct platform_device *dev, u32 id, u32 thresh,
        u32 timeout, u32 *value, struct timespec *ts);
 
-void nvhost_scale3d_set_throughput_hint(int hint);
-
 /* Hacky way to get access to struct nvhost_device_data for VI device. */
 extern struct nvhost_device_data t20_vi_info;
 extern struct nvhost_device_data t30_vi_info;
diff --git a/include/linux/tegra-throughput.h b/include/linux/tegra-throughput.h
new file mode 100644 (file)
index 0000000..1fe692b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef TEGRA_THROUGHPUT
+#define TEGRA_THROUGHPUT
+
+#include <linux/notifier.h>
+
+extern struct blocking_notifier_head throughput_notifier_list;
+int tegra_throughput_get_hint(void);
+
+#endif