summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/host/t30
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2011-09-01 08:05:10 +0300
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:49:11 -0800
commit17bbd9c676aa818a9b28b77b84c1e28cd080b08e (patch)
tree98db15a91924ee0670afba961658a7a7a79d8ae5 /drivers/video/tegra/host/t30
parent4faee0163e39b1bb91b7e987732ae63884c11f88 (diff)
nvhost: Modularize ACM code
Refactor nvhost_acm.c so that module specific code can be separated from generic code: * Module clock and power op descriptions added to channelmap table * New module busy/idle interface added * 3D clock scaling for Tegra3 moved behind the module busy/idle API * 3D power off code moved to 3dctx where it belongs * Module power on API removed as there were no users * Get/Set rate moved to Tegra3 specific file Bug 870791 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: http://git-master/r/51275 Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com> (cherry-picked from ebea06768d9c9d351a7d1c8dc6499c97f2f5002d) Change-Id: I5857c7db4bbf936a694239a4a3493f2cb95426a1 Reviewed-on: http://git-master/r/56268 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com> Rebase-Id: Reaccd277c8f4fe12a4f7453cc4e787334122a3b8
Diffstat (limited to 'drivers/video/tegra/host/t30')
-rw-r--r--drivers/video/tegra/host/t30/Makefile3
-rw-r--r--drivers/video/tegra/host/t30/acm_t30.c141
-rw-r--r--drivers/video/tegra/host/t30/channel_t30.c126
-rw-r--r--drivers/video/tegra/host/t30/debug_t30.c28
-rw-r--r--drivers/video/tegra/host/t30/scale3d.c354
-rw-r--r--drivers/video/tegra/host/t30/scale3d.h49
-rw-r--r--drivers/video/tegra/host/t30/t30.c5
-rw-r--r--drivers/video/tegra/host/t30/t30.h2
8 files changed, 704 insertions, 4 deletions
diff --git a/drivers/video/tegra/host/t30/Makefile b/drivers/video/tegra/host/t30/Makefile
index 8f4eb4191da6..7e77d8af701c 100644
--- a/drivers/video/tegra/host/t30/Makefile
+++ b/drivers/video/tegra/host/t30/Makefile
@@ -1,6 +1,9 @@
nvhost-t30-objs = \
t30.o \
channel_t30.o \
+ scale3d.o \
+ debug_t30.o \
+ acm_t30.o \
3dctx_t30.o
obj-$(CONFIG_TEGRA_GRHOST) += nvhost-t30.o
diff --git a/drivers/video/tegra/host/t30/acm_t30.c b/drivers/video/tegra/host/t30/acm_t30.c
new file mode 100644
index 000000000000..6c058be9c734
--- /dev/null
+++ b/drivers/video/tegra/host/t30/acm_t30.c
@@ -0,0 +1,141 @@
+/*
+ * drivers/video/tegra/host/t30/acm_t30.c
+ *
+ * Tegra Graphics Host Power Management for Tegra3
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include "../dev.h"
+#include "../chip_support.h"
+#include "../nvhost_acm.h"
+#include "t30.h"
+DEFINE_MUTEX(client_list_lock);
+
+struct nvhost_module_client {
+ struct list_head node;
+ unsigned long rate[NVHOST_MODULE_MAX_CLOCKS];
+ void *priv;
+};
+
+int t30_acm_get_rate(struct nvhost_module *mod, unsigned long *rate,
+ int index)
+{
+ struct clk *c;
+
+ c = mod->clk[index];
+ if (IS_ERR_OR_NULL(c))
+ return -EINVAL;
+
+ /* Need to enable client to get correct rate */
+ nvhost_module_busy(mod);
+ *rate = clk_get_rate(c);
+ nvhost_module_idle(mod);
+ return 0;
+}
+
+static int t30_acm_update_rate(struct nvhost_module *mod, int index)
+{
+ unsigned long rate = 0;
+ struct nvhost_module_client *m;
+
+ if (!mod->clk[index])
+ return -EINVAL;
+
+ list_for_each_entry(m, &mod->client_list, node) {
+ rate = max(m->rate[index], rate);
+ }
+ if (!rate)
+ rate = clk_round_rate(mod->clk[index],
+ mod->desc->clocks[index].default_rate);
+
+ return clk_set_rate(mod->clk[index], rate);
+}
+
+int t30_acm_set_rate(struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index)
+{
+ struct nvhost_module_client *m;
+ int ret;
+
+ mutex_lock(&client_list_lock);
+ list_for_each_entry(m, &mod->client_list, node) {
+ if (m->priv == priv) {
+ rate = clk_round_rate(mod->clk[index], rate);
+ m->rate[index] = rate;
+ break;
+ }
+ }
+ ret = t30_acm_update_rate(mod, index);
+ mutex_unlock(&client_list_lock);
+ return ret;
+}
+
+int t30_acm_add_client(struct nvhost_module *mod, void *priv)
+{
+ int i;
+ unsigned long rate;
+ struct nvhost_module_client *client;
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&client->node);
+ client->priv = priv;
+
+ for (i = 0; i < mod->num_clks; i++) {
+ rate = clk_round_rate(mod->clk[i],
+ mod->desc->clocks[i].default_rate);
+ client->rate[i] = rate;
+ }
+ mutex_lock(&client_list_lock);
+ list_add_tail(&client->node, &mod->client_list);
+ mutex_unlock(&client_list_lock);
+ return 0;
+}
+
+void t30_acm_remove_client(struct nvhost_module *mod, void *priv)
+{
+ int i;
+ struct nvhost_module_client *m;
+
+ mutex_lock(&client_list_lock);
+ list_for_each_entry(m, &mod->client_list, node) {
+ if (priv == m->priv) {
+ list_del(&m->node);
+ break;
+ }
+ }
+ m->priv = NULL;
+ kfree(m);
+ for (i = 0; i < mod->num_clks; i++)
+ t30_acm_update_rate(mod, i);
+ mutex_unlock(&client_list_lock);
+}
+
+int nvhost_init_t30_acm(struct nvhost_master *host)
+{
+ host->op.acm.get_rate = t30_acm_get_rate;
+ host->op.acm.set_rate = t30_acm_set_rate;
+ host->op.acm.add_client = t30_acm_add_client;
+ host->op.acm.remove_client = t30_acm_remove_client;
+
+ return 0;
+}
diff --git a/drivers/video/tegra/host/t30/channel_t30.c b/drivers/video/tegra/host/t30/channel_t30.c
index 6ec2f39c5711..43cd459bd210 100644
--- a/drivers/video/tegra/host/t30/channel_t30.c
+++ b/drivers/video/tegra/host/t30/channel_t30.c
@@ -20,11 +20,132 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-#include "3dctx_t30.h"
+#include <linux/mutex.h>
+#include <mach/powergate.h>
#include "../dev.h"
#include "../t20/channel_t20.h"
#include "../t20/hardware_t20.h"
#include "../t20/t20.h"
+#include "../t20/syncpt_t20.h"
+#include "../3dctx_common.h"
+#include "3dctx_t30.h"
+#include "scale3d.h"
+
+#define NVMODMUTEX_2D_FULL (1)
+#define NVMODMUTEX_2D_SIMPLE (2)
+#define NVMODMUTEX_2D_SB_A (3)
+#define NVMODMUTEX_2D_SB_B (4)
+#define NVMODMUTEX_3D (5)
+#define NVMODMUTEX_DISPLAYA (6)
+#define NVMODMUTEX_DISPLAYB (7)
+#define NVMODMUTEX_VI (8)
+#define NVMODMUTEX_DSI (9)
+#define NV_FIFO_READ_TIMEOUT 200000
+
+#define HOST_EMC_FLOOR 300000000
+#ifndef TEGRA_POWERGATE_3D1
+#define TEGRA_POWERGATE_3D1 -1
+#endif
+
+const struct nvhost_channeldesc nvhost_t30_channelmap[] = {
+{
+ /* channel 0 */
+ .name = "display",
+ .syncpts = BIT(NVSYNCPT_DISP0_A) | BIT(NVSYNCPT_DISP1_A) |
+ BIT(NVSYNCPT_DISP0_B) | BIT(NVSYNCPT_DISP1_B) |
+ BIT(NVSYNCPT_DISP0_C) | BIT(NVSYNCPT_DISP1_C) |
+ BIT(NVSYNCPT_VBLANK0) | BIT(NVSYNCPT_VBLANK1),
+ .modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB),
+ .module = {
+ NVHOST_MODULE_NO_POWERGATING,
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
+},
+{
+ /* channel 1 */
+ .name = "gr3d",
+ .syncpts = BIT(NVSYNCPT_3D),
+ .waitbases = BIT(NVWAITBASE_3D),
+ .modulemutexes = BIT(NVMODMUTEX_3D),
+ .class = NV_GRAPHICS_3D_CLASS_ID,
+ .module = {
+ .prepare_poweroff = nvhost_3dctx_prepare_power_off,
+ .busy = nvhost_scale3d_notify_busy,
+ .idle = nvhost_scale3d_notify_idle,
+ .init = nvhost_scale3d_init,
+ .deinit = nvhost_scale3d_deinit,
+ .suspend = nvhost_scale3d_suspend,
+ .clocks = {{"gr3d", UINT_MAX},
+ {"gr3d2", UINT_MAX},
+ {"emc", HOST_EMC_FLOOR} },
+ .powergate_ids = {TEGRA_POWERGATE_3D,
+ TEGRA_POWERGATE_3D1},
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
+},
+{
+ /* channel 2 */
+ .name = "gr2d",
+ .syncpts = BIT(NVSYNCPT_2D_0) | BIT(NVSYNCPT_2D_1),
+ .waitbases = BIT(NVWAITBASE_2D_0) | BIT(NVWAITBASE_2D_1),
+ .modulemutexes = BIT(NVMODMUTEX_2D_FULL) | BIT(NVMODMUTEX_2D_SIMPLE) |
+ BIT(NVMODMUTEX_2D_SB_A) | BIT(NVMODMUTEX_2D_SB_B),
+ .module = {
+ .clocks = {{"gr2d", 0},
+ {"epp", 0},
+ {"emc", HOST_EMC_FLOOR} },
+ NVHOST_MODULE_NO_POWERGATING,
+ .powerdown_delay = 0,
+ },
+},
+{
+ /* channel 3 */
+ .name = "isp",
+ .syncpts = 0,
+ .module = {
+ NVHOST_MODULE_NO_POWERGATING,
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
+},
+{
+ /* channel 4 */
+ .name = "vi",
+ .syncpts = BIT(NVSYNCPT_CSI_VI_0) | BIT(NVSYNCPT_CSI_VI_1) |
+ BIT(NVSYNCPT_VI_ISP_0) | BIT(NVSYNCPT_VI_ISP_1) |
+ BIT(NVSYNCPT_VI_ISP_2) | BIT(NVSYNCPT_VI_ISP_3) |
+ BIT(NVSYNCPT_VI_ISP_4),
+ .modulemutexes = BIT(NVMODMUTEX_VI),
+ .exclusive = true,
+ .module = {
+ NVHOST_MODULE_NO_POWERGATING,
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
+},
+{
+ /* channel 5 */
+ .name = "mpe",
+ .syncpts = BIT(NVSYNCPT_MPE) | BIT(NVSYNCPT_MPE_EBM_EOF) |
+ BIT(NVSYNCPT_MPE_WR_SAFE),
+ .waitbases = BIT(NVWAITBASE_MPE),
+ .class = NV_VIDEO_ENCODE_MPEG_CLASS_ID,
+ .exclusive = true,
+ .keepalive = true,
+ .module = {
+ .clocks = {{"mpe", UINT_MAX}, {"emc", UINT_MAX}, {} },
+ .powergate_ids = {TEGRA_POWERGATE_MPE, -1},
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
+},
+{
+ /* channel 6 */
+ .name = "dsi",
+ .syncpts = BIT(NVSYNCPT_DSI),
+ .modulemutexes = BIT(NVMODMUTEX_DSI),
+ .module = {
+ NVHOST_MODULE_NO_POWERGATING,
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
+} };
#define NVHOST_CHANNEL_BASE 0
@@ -46,13 +167,12 @@ static inline void __iomem *t30_channel_aperture(void __iomem *p, int ndx)
return p;
}
-
static int t30_channel_init(struct nvhost_channel *ch,
struct nvhost_master *dev, int index)
{
ch->dev = dev;
ch->chid = index;
- ch->desc = nvhost_t20_channelmap + index;
+ ch->desc = nvhost_t30_channelmap + index;
mutex_init(&ch->reflock);
mutex_init(&ch->submitlock);
diff --git a/drivers/video/tegra/host/t30/debug_t30.c b/drivers/video/tegra/host/t30/debug_t30.c
new file mode 100644
index 000000000000..b7d88a852780
--- /dev/null
+++ b/drivers/video/tegra/host/t30/debug_t30.c
@@ -0,0 +1,28 @@
+/*
+ * drivers/video/tegra/host/t30/debug_t30.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include "../dev.h"
+#include "scale3d.h"
+#include "../t20/t20.h"
+#include "../chip_support.h"
+
+int nvhost_init_t30_debug_support(struct nvhost_master *host)
+{
+ nvhost_init_t20_debug_support(host);
+ host->op.debug.debug_init = nvhost_scale3d_debug_init;
+
+ return 0;
+}
diff --git a/drivers/video/tegra/host/t30/scale3d.c b/drivers/video/tegra/host/t30/scale3d.c
new file mode 100644
index 000000000000..63d52abf2bdf
--- /dev/null
+++ b/drivers/video/tegra/host/t30/scale3d.c
@@ -0,0 +1,354 @@
+/*
+ * drivers/video/tegra/host/t20/scale3d.c
+ *
+ * Tegra Graphics Host 3D clock scaling
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+/*
+ * 3d clock scaling
+ *
+ * module3d_notify_busy() is called upon submit, module3d_notify_idle() is
+ * called when all outstanding submits are completed. Idle times are measured
+ * over a fixed time period (scale3d.p_period). If the 3d module idle time
+ * percentage goes over the limit (set in scale3d.p_idle_max), 3d clocks are
+ * scaled down. If the percentage goes under the minimum limit (set in
+ * scale3d.p_idle_min), 3d clocks are scaled up. An additional test is made
+ * over the time frame given in scale3d.p_fast_response for clocking up
+ * quickly in response to sudden load peaks.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <mach/clk.h>
+#include <mach/hardware.h>
+#include "scale3d.h"
+#include "../dev.h"
+
+static int scale3d_is_enabled(void);
+static void scale3d_enable(int enable);
+
+/*
+ * debugfs parameters to control 3d clock scaling test
+ *
+ * period - time period for clock rate evaluation
+ * fast_response - time period for evaluation of 'busy' spikes
+ * idle_min - if less than [idle_min] percent idle over [fast_response]
+ * microseconds, clock up.
+ * idle_max - if over [idle_max] percent idle over [period] microseconds,
+ * clock down.
+ * max_scale - limits rate changes to no less than (100 - max_scale)% or
+ * (100 + 2 * max_scale)% of current clock rate
+ * verbosity - set above 5 for debug printouts
+ */
+
+struct scale3d_info_rec {
+ struct mutex lock; /* lock for timestamps etc */
+ int enable;
+ int init;
+ ktime_t idle_frame;
+ ktime_t fast_frame;
+ ktime_t last_idle;
+ ktime_t last_busy;
+ int is_idle;
+ unsigned long idle_total;
+ struct work_struct work;
+ unsigned int scale;
+ unsigned int p_period;
+ unsigned int p_idle_min;
+ unsigned int p_idle_max;
+ unsigned int p_fast_response;
+ unsigned int p_verbosity;
+ struct clk *clk_3d;
+ struct clk *clk_3d2;
+};
+
+static struct scale3d_info_rec scale3d;
+
+static void scale3d_clocks(unsigned long percent)
+{
+ unsigned long hz, curr;
+
+ if (!tegra_is_clk_enabled(scale3d.clk_3d))
+ return;
+
+ if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
+ if (!tegra_is_clk_enabled(scale3d.clk_3d2))
+ return;
+
+ curr = clk_get_rate(scale3d.clk_3d);
+ hz = percent * (curr / 100);
+
+ if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
+ clk_set_rate(scale3d.clk_3d2, 0);
+ clk_set_rate(scale3d.clk_3d, hz);
+}
+
+static void scale3d_clocks_handler(struct work_struct *work)
+{
+ unsigned int scale;
+
+ mutex_lock(&scale3d.lock);
+ scale = scale3d.scale;
+ mutex_unlock(&scale3d.lock);
+
+ if (scale != 0)
+ scale3d_clocks(scale);
+}
+
+void nvhost_scale3d_suspend(struct nvhost_module *mod)
+{
+ cancel_work_sync(&scale3d.work);
+}
+
+/* set 3d clocks to max */
+static void reset_3d_clocks(void)
+{
+ unsigned long hz;
+
+ hz = clk_round_rate(scale3d.clk_3d, UINT_MAX);
+ clk_set_rate(scale3d.clk_3d, hz);
+ if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
+ clk_set_rate(scale3d.clk_3d2, hz);
+}
+
+static int scale3d_is_enabled(void)
+{
+ int enable;
+
+ mutex_lock(&scale3d.lock);
+ enable = scale3d.enable;
+ mutex_unlock(&scale3d.lock);
+
+ return enable;
+}
+
+static void scale3d_enable(int enable)
+{
+ int disable = 0;
+
+ mutex_lock(&scale3d.lock);
+
+ if (enable)
+ scale3d.enable = 1;
+ else {
+ scale3d.enable = 0;
+ disable = 1;
+ }
+
+ mutex_unlock(&scale3d.lock);
+
+ if (disable)
+ reset_3d_clocks();
+}
+
+static void reset_scaling_counters(ktime_t time)
+{
+ scale3d.idle_total = 0;
+ scale3d.last_idle = time;
+ scale3d.last_busy = time;
+ scale3d.idle_frame = time;
+}
+
+static void scaling_state_check(ktime_t time)
+{
+ unsigned long dt;
+
+ /* check for load peaks */
+ dt = (unsigned long) ktime_us_delta(time, scale3d.fast_frame);
+ if (dt > scale3d.p_fast_response) {
+ unsigned long idleness = (scale3d.idle_total * 100) / dt;
+ scale3d.fast_frame = time;
+ /* if too busy, scale up */
+ if (idleness < scale3d.p_idle_min) {
+ if (scale3d.p_verbosity > 5)
+ pr_info("scale3d: %ld%% busy\n",
+ 100 - idleness);
+
+ scale3d.scale = 200;
+ schedule_work(&scale3d.work);
+ reset_scaling_counters(time);
+ return;
+ }
+ }
+
+ dt = (unsigned long) ktime_us_delta(time, scale3d.idle_frame);
+ if (dt > scale3d.p_period) {
+ unsigned long idleness = (scale3d.idle_total * 100) / dt;
+
+ if (scale3d.p_verbosity > 5)
+ pr_info("scale3d: idle %lu, ~%lu%%\n",
+ scale3d.idle_total, idleness);
+
+ if (idleness > scale3d.p_idle_max) {
+ /* if idle time is high, clock down */
+ scale3d.scale = 100 - (idleness - scale3d.p_idle_min);
+ schedule_work(&scale3d.work);
+ } else if (idleness < scale3d.p_idle_min) {
+ /* if idle time is low, clock up */
+ scale3d.scale = 200;
+ schedule_work(&scale3d.work);
+ }
+ reset_scaling_counters(time);
+ }
+}
+
+void nvhost_scale3d_notify_idle(struct nvhost_module *mod)
+{
+ mutex_lock(&scale3d.lock);
+
+ if (!scale3d.enable)
+ goto done;
+
+ scale3d.last_idle = ktime_get();
+ scale3d.is_idle = 1;
+
+ scaling_state_check(scale3d.last_idle);
+
+done:
+ mutex_unlock(&scale3d.lock);
+}
+
+void nvhost_scale3d_notify_busy(struct nvhost_module *mod)
+{
+ unsigned long idle;
+ ktime_t t;
+
+ mutex_lock(&scale3d.lock);
+
+ if (!scale3d.enable)
+ goto done;
+
+ t = ktime_get();
+
+ if (scale3d.is_idle) {
+ scale3d.last_busy = t;
+ idle = (unsigned long)
+ ktime_us_delta(scale3d.last_busy, scale3d.last_idle);
+ scale3d.idle_total += idle;
+ scale3d.is_idle = 0;
+ }
+
+ scaling_state_check(t);
+
+done:
+ mutex_unlock(&scale3d.lock);
+}
+
+void nvhost_scale3d_reset()
+{
+ ktime_t t = ktime_get();
+ mutex_lock(&scale3d.lock);
+ reset_scaling_counters(t);
+ mutex_unlock(&scale3d.lock);
+}
+
+/*
+ * debugfs parameters to control 3d clock scaling
+ */
+
+void nvhost_scale3d_debug_init(struct dentry *de)
+{
+ struct dentry *d, *f;
+
+ d = debugfs_create_dir("scaling", de);
+ if (!d) {
+ pr_err("scale3d: can\'t create debugfs directory\n");
+ return;
+ }
+
+#define CREATE_SCALE3D_FILE(fname) \
+ do {\
+ f = debugfs_create_u32(#fname, S_IRUGO | S_IWUSR, d,\
+ &scale3d.p_##fname);\
+ if (NULL == f) {\
+ pr_err("scale3d: can\'t create file " #fname "\n");\
+ return;\
+ } \
+ } while (0)
+
+ CREATE_SCALE3D_FILE(fast_response);
+ CREATE_SCALE3D_FILE(idle_min);
+ CREATE_SCALE3D_FILE(idle_max);
+ CREATE_SCALE3D_FILE(period);
+ CREATE_SCALE3D_FILE(verbosity);
+#undef CREATE_SCALE3D_FILE
+}
+
+static ssize_t enable_3d_scaling_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t res;
+
+ res = snprintf(buf, PAGE_SIZE, "%d\n", scale3d_is_enabled());
+
+ return res;
+}
+
+static ssize_t enable_3d_scaling_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long val = 0;
+
+ if (strict_strtoul(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ scale3d_enable(val);
+
+ return count;
+}
+
+static DEVICE_ATTR(enable_3d_scaling, S_IRUGO | S_IWUSR,
+ enable_3d_scaling_show, enable_3d_scaling_store);
+
+void nvhost_scale3d_init(struct device *d, struct nvhost_module *mod)
+{
+ if (!scale3d.init) {
+ int error;
+ mutex_init(&scale3d.lock);
+
+ scale3d.clk_3d = mod->clk[0];
+ if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
+ scale3d.clk_3d2 = mod->clk[1];
+
+ INIT_WORK(&scale3d.work, scale3d_clocks_handler);
+
+ /* set scaling parameter defaults */
+ scale3d.enable = 0;
+ scale3d.p_period = 1200000;
+ scale3d.p_idle_min = 17;
+ scale3d.p_idle_max = 17;
+ scale3d.p_fast_response = 16000;
+ scale3d.p_verbosity = 0;
+
+ error = device_create_file(d, &dev_attr_enable_3d_scaling);
+ if (error)
+ dev_err(d, "failed to create sysfs attributes");
+
+ scale3d.init = 1;
+ }
+
+ nvhost_scale3d_reset();
+}
+
+void nvhost_scale3d_deinit(struct device *dev, struct nvhost_module *mod)
+{
+ device_remove_file(dev, &dev_attr_enable_3d_scaling);
+ scale3d.init = 0;
+}
diff --git a/drivers/video/tegra/host/t30/scale3d.h b/drivers/video/tegra/host/t30/scale3d.h
new file mode 100644
index 000000000000..e6d1a40f53e0
--- /dev/null
+++ b/drivers/video/tegra/host/t30/scale3d.h
@@ -0,0 +1,49 @@
+/*
+ * drivers/video/tegra/host/t30/scale3d.h
+ *
+ * Tegra Graphics Host 3D Clock Scaling
+ *
+ * Copyright (c) 2010-2011, NVIDIA Corporation.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 NVHOST_T30_SCALE3D_H
+#define NVHOST_T30_SCALE3D_H
+
+struct nvhost_module;
+struct device;
+struct dentry;
+
+/* Initialization and de-initialization for module */
+void nvhost_scale3d_init(struct device *, struct nvhost_module *);
+void nvhost_scale3d_deinit(struct device *, struct nvhost_module *);
+
+/* Suspend is called when powering down module */
+void nvhost_scale3d_suspend(struct nvhost_module *);
+
+/* reset 3d module load counters, called on resume */
+void nvhost_scale3d_reset(void);
+
+/*
+ * call when performing submit to notify scaling mechanism that 3d module is
+ * in use
+ */
+void nvhost_scale3d_notify_busy(struct nvhost_module *);
+void nvhost_scale3d_notify_idle(struct nvhost_module *);
+
+void nvhost_scale3d_debug_init(struct dentry *de);
+
+#endif
diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c
index c7a5011cb953..00e0f648afcc 100644
--- a/drivers/video/tegra/host/t30/t30.c
+++ b/drivers/video/tegra/host/t30/t30.c
@@ -34,7 +34,7 @@ int nvhost_init_t30_support(struct nvhost_master *host)
err = nvhost_init_t20_cdma_support(host);
if (err)
return err;
- err = nvhost_init_t20_debug_support(host);
+ err = nvhost_init_t30_debug_support(host);
if (err)
return err;
err = nvhost_init_t20_syncpt_support(host);
@@ -46,5 +46,8 @@ int nvhost_init_t30_support(struct nvhost_master *host)
err = nvhost_init_t20_cpuaccess_support(host);
if (err)
return err;
+ err = nvhost_init_t30_acm(host);
+ if (err)
+ return err;
return 0;
}
diff --git a/drivers/video/tegra/host/t30/t30.h b/drivers/video/tegra/host/t30/t30.h
index 3ea69631bf2e..bbc88bbb81f0 100644
--- a/drivers/video/tegra/host/t30/t30.h
+++ b/drivers/video/tegra/host/t30/t30.h
@@ -25,5 +25,7 @@
#include "../t20/t20.h"
int nvhost_init_t30_channel_support(struct nvhost_master *);
+int nvhost_init_t30_debug_support(struct nvhost_master *);
+int nvhost_init_t30_acm(struct nvhost_master *);
#endif /* _NVHOST_T30_H_ */