summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTerje Bergstrom <tbergstrom@nvidia.com>2011-09-01 08:05:10 +0300
committerVarun Colbert <vcolbert@nvidia.com>2011-09-16 14:58:07 -0700
commitebea06768d9c9d351a7d1c8dc6499c97f2f5002d (patch)
tree4c2c95e48ba092c7df92ef81b078de106a15bce2 /drivers
parentf837e5b34d311b13a52996b68b27b0207c15bd79 (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> Change-Id: Ia27db32c606b7dd3f9acf0c7e43e4de80a9ef0b4 Reviewed-on: http://git-master/r/51275 Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/tegra/host/3dctx_common.c55
-rw-r--r--drivers/video/tegra/host/3dctx_common.h18
-rw-r--r--drivers/video/tegra/host/chip_support.h15
-rw-r--r--drivers/video/tegra/host/debug.c4
-rw-r--r--drivers/video/tegra/host/debug.h1
-rw-r--r--drivers/video/tegra/host/dev.c103
-rw-r--r--drivers/video/tegra/host/nvhost_acm.c717
-rw-r--r--drivers/video/tegra/host/nvhost_acm.h73
-rw-r--r--drivers/video/tegra/host/nvhost_channel.c10
-rw-r--r--drivers/video/tegra/host/nvhost_channel.h3
-rw-r--r--drivers/video/tegra/host/nvhost_scale.h73
-rw-r--r--drivers/video/tegra/host/t20/cdma_t20.c3
-rw-r--r--drivers/video/tegra/host/t20/channel_t20.c102
-rw-r--r--drivers/video/tegra/host/t20/t20.h2
-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
22 files changed, 1052 insertions, 835 deletions
diff --git a/drivers/video/tegra/host/3dctx_common.c b/drivers/video/tegra/host/3dctx_common.c
index 59f51ac24f44..05b4c1653b49 100644
--- a/drivers/video/tegra/host/3dctx_common.c
+++ b/drivers/video/tegra/host/3dctx_common.c
@@ -134,3 +134,58 @@ void nvhost_3dctx_put(struct nvhost_hwctx *ctx)
{
kref_put(&ctx->ref, nvhost_3dctx_free);
}
+
+void nvhost_3dctx_prepare_power_off(struct nvhost_module *mod)
+{
+ struct nvhost_channel *ch =
+ container_of(mod, struct nvhost_channel, mod);
+ struct nvhost_hwctx *hwctx_to_save;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
+ u32 syncpt_incrs, syncpt_val;
+ int err;
+ void *ref;
+
+ if (mod->desc->busy)
+ mod->desc->busy(mod);
+
+ mutex_lock(&ch->submitlock);
+ hwctx_to_save = ch->cur_ctx;
+ if (!hwctx_to_save) {
+ mutex_unlock(&ch->submitlock);
+ return;
+ }
+
+ err = nvhost_cdma_begin(&ch->cdma, hwctx_to_save->timeout);
+ if (err) {
+ mutex_unlock(&ch->submitlock);
+ return;
+ }
+
+ hwctx_to_save->valid = true;
+ ch->ctxhandler.get(hwctx_to_save);
+ ch->cur_ctx = NULL;
+
+ syncpt_incrs = hwctx_to_save->save_incrs;
+ syncpt_val = nvhost_syncpt_incr_max(&ch->dev->syncpt,
+ NVSYNCPT_3D, syncpt_incrs);
+
+ ch->ctxhandler.save_push(&ch->cdma, hwctx_to_save);
+ nvhost_cdma_end(&ch->cdma, ch->dev->nvmap, NVSYNCPT_3D, syncpt_val,
+ NULL, 0, hwctx_to_save->timeout);
+
+ nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D,
+ syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh,
+ NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL);
+
+ nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, syncpt_val,
+ NVHOST_INTR_ACTION_WAKEUP, &wq, &ref);
+ wait_event(wq,
+ nvhost_syncpt_min_cmp(&ch->dev->syncpt,
+ NVSYNCPT_3D, syncpt_val));
+
+ nvhost_intr_put_ref(&ch->dev->intr, ref);
+
+ nvhost_cdma_update(&ch->cdma);
+
+ mutex_unlock(&ch->submitlock);
+}
diff --git a/drivers/video/tegra/host/3dctx_common.h b/drivers/video/tegra/host/3dctx_common.h
index 3816b1a7eafd..7df2af29cbe3 100644
--- a/drivers/video/tegra/host/3dctx_common.h
+++ b/drivers/video/tegra/host/3dctx_common.h
@@ -23,6 +23,7 @@
#ifndef __NVHOST_3DCTX_COMMON_H
#define __NVHOST_3DCTX_COMMON_H
+#include "nvhost_acm.h"
#include <linux/types.h>
/* Internal variables used by common 3D context switch functions */
@@ -38,15 +39,16 @@ struct nvhost_channel;
struct kref;
/* Functions used commonly by all 3D context switch modules */
-extern void nvhost_3dctx_restore_begin(u32 *ptr);
-extern void nvhost_3dctx_restore_direct(u32 *ptr, u32 start_reg, u32 count);
-extern void nvhost_3dctx_restore_indirect(u32 *ptr, u32 offset_reg,
+void nvhost_3dctx_restore_begin(u32 *ptr);
+void nvhost_3dctx_restore_direct(u32 *ptr, u32 start_reg, u32 count);
+void nvhost_3dctx_restore_indirect(u32 *ptr, u32 offset_reg,
u32 offset, u32 data_reg, u32 count);
-extern void nvhost_3dctx_restore_end(u32 *ptr);
-extern struct nvhost_hwctx *nvhost_3dctx_alloc_common(
+void nvhost_3dctx_restore_end(u32 *ptr);
+struct nvhost_hwctx *nvhost_3dctx_alloc_common(
struct nvhost_channel *ch, bool map_restore);
-extern void nvhost_3dctx_get(struct nvhost_hwctx *ctx);
-extern void nvhost_3dctx_free(struct kref *ref);
-extern void nvhost_3dctx_put(struct nvhost_hwctx *ctx);
+void nvhost_3dctx_get(struct nvhost_hwctx *ctx);
+void nvhost_3dctx_free(struct kref *ref);
+void nvhost_3dctx_put(struct nvhost_hwctx *ctx);
+void nvhost_3dctx_prepare_power_off(struct nvhost_module *mod);
#endif
diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h
index c9c88620a5c3..8fb2526a66cb 100644
--- a/drivers/video/tegra/host/chip_support.h
+++ b/drivers/video/tegra/host/chip_support.h
@@ -22,6 +22,7 @@
#ifndef _NVHOST_CHIP_SUPPORT_H_
#define _NVHOST_CHIP_SUPPORT_H_
+#include <linux/types.h>
struct output;
struct nvhost_waitchk;
struct nvhost_userctx_timeout;
@@ -36,6 +37,9 @@ struct nvhost_intr;
struct push_buffer;
struct nvhost_syncpt;
struct nvhost_cpuaccess;
+struct nvhost_module;
+struct nvhost_master;
+struct dentry;
struct nvhost_chip_support {
struct {
@@ -103,6 +107,7 @@ struct nvhost_chip_support {
} push_buffer;
struct {
+ void (*debug_init)(struct dentry *de);
void (*show_channel_cdma)(struct nvhost_master *,
struct output *,
int chid);
@@ -147,6 +152,16 @@ struct nvhost_chip_support {
void (*mutex_unlock)(struct nvhost_cpuaccess *,
unsigned int idx);
} cpuaccess;
+
+ struct {
+ int (*add_client)(struct nvhost_module *mod, void *priv);
+ void (*remove_client)(struct nvhost_module *mod, void *priv);
+ int (*get_rate)(struct nvhost_module *mod,
+ unsigned long *rate,
+ int index);
+ int (*set_rate)(struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index);
+ } acm;
};
diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c
index 8892a0072480..b5ee0f797fc6 100644
--- a/drivers/video/tegra/host/debug.c
+++ b/drivers/video/tegra/host/debug.c
@@ -116,7 +116,9 @@ void nvhost_debug_init(struct nvhost_master *master)
debugfs_create_u32("null_kickoff_pid", S_IRUGO|S_IWUSR, de,
&nvhost_debug_null_kickoff_pid);
- nvhost_debug_scale_init(de);
+ if (master->op.debug.debug_init)
+ master->op.debug.debug_init(de);
+
debugfs_create_u32("force_timeout_pid", S_IRUGO|S_IWUSR, de,
&nvhost_debug_force_timeout_pid);
debugfs_create_u32("force_timeout_val", S_IRUGO|S_IWUSR, de,
diff --git a/drivers/video/tegra/host/debug.h b/drivers/video/tegra/host/debug.h
index d3adcc60cf04..6529813ac39f 100644
--- a/drivers/video/tegra/host/debug.h
+++ b/drivers/video/tegra/host/debug.h
@@ -43,7 +43,6 @@ static inline void write_to_printk(void *ctx, const char* str, size_t len)
void nvhost_debug_output(struct output *o, const char* fmt, ...);
-void nvhost_debug_scale_init(struct dentry *de);
extern pid_t nvhost_debug_force_timeout_pid;
extern u32 nvhost_debug_force_timeout_val;
extern u32 nvhost_debug_force_timeout_channel;
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c
index 0a511cb82aa5..26381a93aaea 100644
--- a/drivers/video/tegra/host/dev.c
+++ b/drivers/video/tegra/host/dev.c
@@ -43,7 +43,6 @@
#include <mach/gpufuse.h>
#include <mach/hardware.h>
-#include "nvhost_scale.h"
#include "debug.h"
#define DRIVER_NAME "tegra_grhost"
@@ -82,7 +81,7 @@ static int nvhost_channelrelease(struct inode *inode, struct file *filp)
filp->private_data = NULL;
- nvhost_module_remove_client(&priv->ch->mod, priv);
+ nvhost_module_remove_client(priv->ch->dev, &priv->ch->mod, priv);
nvhost_putchannel(priv->ch, priv->hwctx);
if (priv->hwctx)
@@ -118,7 +117,7 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
}
filp->private_data = priv;
priv->ch = ch;
- nvhost_module_add_client(&ch->mod, priv);
+ nvhost_module_add_client(ch->dev, &ch->mod, priv);
priv->gather_mem = nvmap_alloc(ch->dev->nvmap,
sizeof(u32) * 2 * NVHOST_MAX_GATHERS, 32,
NVMAP_HANDLE_CACHEABLE);
@@ -445,7 +444,8 @@ static long nvhost_channelctl(struct file *filp,
struct nvhost_clk_rate_args *arg =
(struct nvhost_clk_rate_args *)buf;
- err = nvhost_module_get_rate(&priv->ch->mod, &rate, 0);
+ err = nvhost_module_get_rate(priv->ch->dev,
+ &priv->ch->mod, &rate, 0);
if (err == 0)
arg->rate = rate;
break;
@@ -456,7 +456,8 @@ static long nvhost_channelctl(struct file *filp,
(struct nvhost_clk_rate_args *)buf;
unsigned long rate = (unsigned long)arg->rate;
- err = nvhost_module_set_rate(&priv->ch->mod, priv, rate, 0);
+ err = nvhost_module_set_rate(priv->ch->dev,
+ &priv->ch->mod, priv, rate, 0);
break;
}
case NVHOST_IOCTL_CHANNEL_SET_TIMEOUT:
@@ -695,19 +696,24 @@ static struct file_operations nvhost_ctrlops = {
.unlocked_ioctl = nvhost_ctrlctl
};
-static void power_host(struct nvhost_module *mod, enum nvhost_power_action action)
+static void power_on_host(struct nvhost_module *mod)
{
- struct nvhost_master *dev = container_of(mod, struct nvhost_master, mod);
-
- if (action == NVHOST_POWER_ACTION_ON) {
- nvhost_intr_start(&dev->intr, clk_get_rate(mod->clk[0].clk));
- } else if (action == NVHOST_POWER_ACTION_OFF) {
- int i;
- for (i = 0; i < dev->nb_channels; i++)
- nvhost_channel_suspend(&dev->channels[i]);
- nvhost_syncpt_save(&dev->syncpt);
- nvhost_intr_stop(&dev->intr);
- }
+ struct nvhost_master *dev =
+ container_of(mod, struct nvhost_master, mod);
+
+ nvhost_intr_start(&dev->intr, clk_get_rate(mod->clk[0]));
+}
+
+static void power_off_host(struct nvhost_module *mod)
+{
+ struct nvhost_master *dev =
+ container_of(mod, struct nvhost_master, mod);
+ int i;
+
+ for (i = 0; i < dev->nb_channels; i++)
+ nvhost_channel_suspend(&dev->channels[i]);
+ nvhost_syncpt_save(&dev->syncpt);
+ nvhost_intr_stop(&dev->intr);
}
static int __devinit nvhost_user_init(struct nvhost_master *host)
@@ -857,44 +863,12 @@ static int __devinit nvhost_init_chip_support(struct nvhost_master *host)
return 0;
}
-
-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_remove_sysfs(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_enable_3d_scaling);
-}
-
-void nvhost_create_sysfs(struct device *dev)
-{
- int error = device_create_file(dev, &dev_attr_enable_3d_scaling);
- if (error)
- dev_err(dev, "failed to create sysfs attributes");
-}
+struct nvhost_moduledesc hostdesc = {
+ .finalize_poweron = power_on_host,
+ .prepare_poweroff = power_off_host,
+ .clocks = {{"host1x", UINT_MAX}, {} },
+ NVHOST_MODULE_NO_POWERGATING,
+};
static int __devinit nvhost_probe(struct platform_device *pdev)
{
@@ -966,23 +940,22 @@ static int __devinit nvhost_probe(struct platform_device *pdev)
if (err)
goto fail;
- err = nvhost_module_init(&host->mod, "host1x", power_host, NULL, &pdev->dev);
+ err = nvhost_module_init(&host->mod, "host1x",
+ &hostdesc, NULL, &pdev->dev);
if (err)
goto fail;
platform_set_drvdata(pdev, host);
- clk_enable(host->mod.clk[0].clk);
+ clk_enable(host->mod.clk[0]);
nvhost_syncpt_reset(&host->syncpt);
- clk_disable(host->mod.clk[0].clk);
+ clk_disable(host->mod.clk[0]);
nvhost_bus_register(host);
nvhost_debug_init(host);
- nvhost_create_sysfs(&pdev->dev);
-
dev_info(&pdev->dev, "initialized\n");
return 0;
@@ -998,7 +971,6 @@ static int __exit nvhost_remove(struct platform_device *pdev)
{
struct nvhost_master *host = platform_get_drvdata(pdev);
nvhost_remove_chip_support(host);
- nvhost_remove_sysfs(&pdev->dev);
return 0;
}
@@ -1007,9 +979,9 @@ static int nvhost_suspend(struct platform_device *pdev, pm_message_t state)
struct nvhost_master *host = platform_get_drvdata(pdev);
dev_info(&pdev->dev, "suspending\n");
nvhost_module_suspend(&host->mod, true);
- clk_enable(host->mod.clk[0].clk);
+ clk_enable(host->mod.clk[0]);
nvhost_syncpt_save(&host->syncpt);
- clk_disable(host->mod.clk[0].clk);
+ clk_disable(host->mod.clk[0]);
dev_info(&pdev->dev, "suspended\n");
return 0;
}
@@ -1018,10 +990,9 @@ static int nvhost_resume(struct platform_device *pdev)
{
struct nvhost_master *host = platform_get_drvdata(pdev);
dev_info(&pdev->dev, "resuming\n");
- clk_enable(host->mod.clk[0].clk);
+ clk_enable(host->mod.clk[0]);
nvhost_syncpt_reset(&host->syncpt);
- clk_disable(host->mod.clk[0].clk);
- scale3d_reset();
+ clk_disable(host->mod.clk[0]);
dev_info(&pdev->dev, "resumed\n");
return 0;
}
diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c
index 7ead7ac69fca..4af2cb2b9e5e 100644
--- a/drivers/video/tegra/host/nvhost_acm.c
+++ b/drivers/video/tegra/host/nvhost_acm.c
@@ -20,6 +20,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "nvhost_acm.h"
#include "dev.h"
#include <linux/slab.h>
#include <linux/string.h>
@@ -30,318 +31,124 @@
#include <mach/powergate.h>
#include <mach/clk.h>
#include <mach/hardware.h>
-#include <linux/debugfs.h>
-#include "nvhost_scale.h"
-#define ACM_POWERDOWN_HANDLER_DELAY_MSEC 25
#define ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT (2 * HZ)
#define POWERGATE_DELAY 10
-#define HOST_EMC_FLOOR 300000000
#define MAX_DEVID_LENGTH 16
-void nvhost_module_reset(struct nvhost_module *mod)
+void nvhost_module_reset(struct device *dev, struct nvhost_module *mod)
{
- struct nvhost_master *dev;
- dev = container_of(mod, struct nvhost_channel, mod)->dev;
-
- dev_dbg(&dev->pdev->dev,
+ dev_dbg(dev,
"%s: asserting %s module reset (id %d, id2 %d)\n",
__func__, mod->name,
- mod->powergate_id, mod->powergate_id2);
+ mod->desc->powergate_ids[0], mod->desc->powergate_ids[1]);
/* assert module and mc client reset */
- if (mod->powergate_id != -1) {
- tegra_powergate_mc_disable(mod->powergate_id);
- tegra_periph_reset_assert(mod->clk[0].clk);
- tegra_powergate_mc_flush(mod->powergate_id);
+ if (mod->desc->powergate_ids[0] != -1) {
+ tegra_powergate_mc_disable(mod->desc->powergate_ids[0]);
+ tegra_periph_reset_assert(mod->clk[0]);
+ tegra_powergate_mc_flush(mod->desc->powergate_ids[0]);
}
- if (mod->powergate_id2 != -1) {
- tegra_powergate_mc_disable(mod->powergate_id2);
- tegra_periph_reset_assert(mod->clk[1].clk);
- tegra_powergate_mc_flush(mod->powergate_id2);
+ if (mod->desc->powergate_ids[1] != -1) {
+ tegra_powergate_mc_disable(mod->desc->powergate_ids[1]);
+ tegra_periph_reset_assert(mod->clk[1]);
+ tegra_powergate_mc_flush(mod->desc->powergate_ids[1]);
}
udelay(POWERGATE_DELAY);
/* deassert reset */
- if (mod->powergate_id != -1) {
- tegra_powergate_mc_flush_done(mod->powergate_id);
- tegra_periph_reset_deassert(mod->clk[0].clk);
- tegra_powergate_mc_enable(mod->powergate_id);
+ if (mod->desc->powergate_ids[0] != -1) {
+ tegra_powergate_mc_flush_done(mod->desc->powergate_ids[0]);
+ tegra_periph_reset_deassert(mod->clk[0]);
+ tegra_powergate_mc_enable(mod->desc->powergate_ids[0]);
}
- if (mod->powergate_id2 != -1) {
- tegra_powergate_mc_flush_done(mod->powergate_id2);
- tegra_periph_reset_deassert(mod->clk[1].clk);
- tegra_powergate_mc_enable(mod->powergate_id2);
+ if (mod->desc->powergate_ids[1] != -1) {
+ tegra_powergate_mc_flush_done(mod->desc->powergate_ids[1]);
+ tegra_periph_reset_deassert(mod->clk[1]);
+ tegra_powergate_mc_enable(mod->desc->powergate_ids[1]);
}
- dev_dbg(&dev->pdev->dev, "%s: module %s out of reset\n",
+ dev_dbg(dev, "%s: module %s out of reset\n",
__func__, mod->name);
}
-void nvhost_module_busy(struct nvhost_module *mod)
-{
- mutex_lock(&mod->lock);
- cancel_delayed_work(&mod->powerdown);
- if ((atomic_inc_return(&mod->refcount) == 1) && !mod->powered) {
- int i = 0;
- if (mod->parent)
- nvhost_module_busy(mod->parent);
- if (mod->can_powergate) {
- if (mod->powergate_id != -1)
- tegra_unpowergate_partition(mod->powergate_id);
- if (mod->powergate_id2 != -1)
- tegra_unpowergate_partition(mod->powergate_id2);
- }
- while (i < mod->num_clks)
- clk_enable(mod->clk[i++].clk);
- if (mod->func)
- mod->func(mod, NVHOST_POWER_ACTION_ON);
- mod->powered = true;
- }
- mutex_unlock(&mod->lock);
-}
-
-static void powerdown_handler(struct work_struct *work)
-{
- struct nvhost_module *mod;
-
- mod = container_of(to_delayed_work(work), struct nvhost_module, powerdown);
- mutex_lock(&mod->lock);
- if ((atomic_read(&mod->refcount) == 0) && mod->powered) {
- int i;
- if (mod->func)
- mod->func(mod, NVHOST_POWER_ACTION_OFF);
- for (i = 0; i < mod->num_clks; i++)
- clk_disable(mod->clk[i].clk);
- if (mod->can_powergate) {
- if (mod->powergate_id != -1)
- tegra_powergate_partition(mod->powergate_id);
- if (mod->powergate_id2 != -1)
- tegra_powergate_partition(mod->powergate_id2);
- }
- mod->powered = false;
- if (mod->parent)
- nvhost_module_idle(mod->parent);
- }
- mutex_unlock(&mod->lock);
-}
-
-/*
- * 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.
- */
-static struct scale3d_info_rec scale3d;
-
-static void scale_3d_clocks(unsigned long percent)
+static void clock_enable(struct nvhost_module *mod)
{
- 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);
+ int i;
+ for (i = 0; i < mod->num_clks; i++)
+ clk_enable(mod->clk[i]);
}
-static void scale_3d_clocks_handler(struct work_struct *work)
+static void unpowergate(struct nvhost_module *mod)
{
- unsigned int scale;
+ const struct nvhost_moduledesc *desc = mod->desc;
+ if (desc->can_powergate) {
+ if (desc->powergate_ids[0] != -1)
+ tegra_unpowergate_partition(desc->powergate_ids[0]);
- spin_lock(&scale3d.lock);
- scale = scale3d.scale;
- spin_unlock(&scale3d.lock);
-
- if (scale != 0) {
- mutex_lock(&scale3d.set_lock);
- scale_3d_clocks(scale);
- mutex_unlock(&scale3d.set_lock);
+ if (desc->powergate_ids[1] != -1)
+ tegra_unpowergate_partition(desc->powergate_ids[1]);
}
}
-static void scale3d_init(struct nvhost_module *mod)
-{
- spin_lock_init(&scale3d.lock);
- mutex_init(&scale3d.set_lock);
-
- scale3d.clk_3d = mod->clk[0].clk;
- if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
- scale3d.clk_3d2 = mod->clk[1].clk;
-
- INIT_WORK(&scale3d.work, scale_3d_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;
-
- scale3d_reset();
-
- scale3d.init = 1;
-}
-
-/* set 3d clocks to max */
-static void reset_3d_clocks(void)
-{
- unsigned long hz;
-
- mutex_lock(&scale3d.set_lock);
- 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);
- mutex_unlock(&scale3d.set_lock);
-}
-
-int scale3d_is_enabled(void)
-{
- int enable;
-
- spin_lock(&scale3d.lock);
- enable = scale3d.enable;
- spin_unlock(&scale3d.lock);
-
- return enable;
-}
-
-void scale3d_enable(int enable)
+void nvhost_module_busy(struct nvhost_module *mod)
{
- int disable = 0;
-
- spin_lock(&scale3d.lock);
+ mutex_lock(&mod->lock);
+ cancel_delayed_work(&mod->powerdown);
+ if (mod->desc->busy)
+ mod->desc->busy(mod);
- if (enable)
- scale3d.enable = 1;
- else {
- scale3d.enable = 0;
- disable = 1;
+ if ((atomic_inc_return(&mod->refcount) == 1) && !mod->powered) {
+ if (mod->parent)
+ nvhost_module_busy(mod->parent);
+ unpowergate(mod);
+ clock_enable(mod);
+ if (mod->desc->finalize_poweron)
+ mod->desc->finalize_poweron(mod);
+ mod->powered = true;
}
-
- spin_unlock(&scale3d.lock);
-
- if (disable)
- reset_3d_clocks();
+ mutex_unlock(&mod->lock);
}
-static void reset_scaling_counters(ktime_t time)
+static void clock_disable(struct nvhost_module *mod)
{
- scale3d.idle_total = 0;
- scale3d.last_idle = time;
- scale3d.last_busy = time;
- scale3d.idle_frame = time;
+ int i;
+ for (i = 0; i < mod->num_clks; i++)
+ clk_disable(mod->clk[i]);
}
-static void scaling_state_check(ktime_t time)
+static void powergate(struct nvhost_module *mod)
{
- 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;
- }
- }
+ if (mod->desc->can_powergate) {
+ if (mod->desc->powergate_ids[0] != -1)
+ tegra_powergate_partition(mod->desc->powergate_ids[0]);
- 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);
+ if (mod->desc->powergate_ids[1] != -1)
+ tegra_powergate_partition(mod->desc->powergate_ids[1]);
}
}
-static void module3d_notify_idle(void)
-{
- spin_lock(&scale3d.lock);
-
- if (!scale3d.enable)
- goto done;
-
- scale3d.last_idle = ktime_get();
- scale3d.is_idle = 1;
-
- scaling_state_check(scale3d.last_idle);
-
-done:
- spin_unlock(&scale3d.lock);
-}
-
-void module3d_notify_busy(void)
+static void powerdown_handler(struct work_struct *work)
{
- unsigned long idle;
- ktime_t t;
-
- spin_lock(&scale3d.lock);
-
- if (!scale3d.enable)
- goto done;
-
- t = ktime_get();
+ struct nvhost_module *mod;
- 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;
+ mod = container_of(to_delayed_work(work),
+ struct nvhost_module,
+ powerdown);
+ mutex_lock(&mod->lock);
+ if ((atomic_read(&mod->refcount) == 0) && mod->powered) {
+ if (mod->desc->prepare_poweroff)
+ mod->desc->prepare_poweroff(mod);
+ clock_disable(mod);
+ powergate(mod);
+ mod->powered = false;
+ if (mod->parent)
+ nvhost_module_idle(mod->parent);
}
-
- scaling_state_check(t);
-
-done:
- spin_unlock(&scale3d.lock);
+ mutex_unlock(&mod->lock);
}
-void scale3d_reset()
-{
- ktime_t t = ktime_get();
- spin_lock(&scale3d.lock);
- reset_scaling_counters(t);
- spin_unlock(&scale3d.lock);
-}
void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
{
@@ -351,7 +158,7 @@ void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
if (atomic_sub_return(refs, &mod->refcount) == 0) {
BUG_ON(!mod->powered);
schedule_delayed_work(&mod->powerdown,
- msecs_to_jiffies(mod->powerdown_delay));
+ msecs_to_jiffies(mod->desc->powerdown_delay));
kick = true;
}
mutex_unlock(&mod->lock);
@@ -359,244 +166,50 @@ void nvhost_module_idle_mult(struct nvhost_module *mod, int refs)
if (kick) {
wake_up(&mod->idle);
- if (strcmp(mod->name, "gr3d") == 0)
- module3d_notify_idle();
- }
-}
-
-static const char *get_module_clk_id_tegra2(const char *module, int index,
- struct nvhost_module_clock_info *info)
-{
- const char *name = NULL;
- if (index == 0)
- name = module;
- if (strcmp(module, "gr2d") == 0) {
- if (index == 1)
- name = "epp";
- if (index == 2)
- name = "emc";
- }
- if (strcmp(module, "gr3d") == 0) {
- if (index == 1)
- name = "emc";
- }
- if (strcmp(module, "mpe") == 0) {
- if (index == 1)
- name = "emc";
- }
-
- if (name)
- info->default_rate = UINT_MAX;
-
- return name;
-}
-
-static const char *get_module_clk_id_tegra3(const char *module, int index,
- struct nvhost_module_clock_info *info)
-{
- const char *name = NULL;
- if (index == 0)
- name = module;
- if (strcmp(module, "gr2d") == 0) {
- if (index == 1)
- name = "epp";
- if (index == 2)
- name = "emc";
- } else if (strcmp(module, "gr3d") == 0) {
- if (index == 1)
- name = "gr3d2";
- if (index == 2)
- name = "emc";
- } else if (strcmp(module, "mpe") == 0) {
- if (index == 1)
- name = "emc";
- }
-
- if (name) {
- if (strcmp(name, "emc") == 0)
- info->default_rate = HOST_EMC_FLOOR;
- else if (strcmp(name, "gr2d") == 0)
- info->default_rate = 0;
- else if (strcmp(name, "epp") == 0)
- info->default_rate = 0;
- else
- info->default_rate = UINT_MAX;
- }
-
- return name;
-}
-
-static const char *get_module_clk(const char *module,
- int index,
- struct device *dev,
- struct nvhost_module_clock_info *info)
-{
- const char *clk_id = NULL;
- char devname[MAX_DEVID_LENGTH];
-
- switch (tegra_get_chipid()) {
- case TEGRA_CHIPID_TEGRA2:
- clk_id = get_module_clk_id_tegra2(module, index, info);
- break;
- case TEGRA_CHIPID_TEGRA3:
- clk_id = get_module_clk_id_tegra3(module, index, info);
- break;
- default:
- BUG();
- break;
- }
-
- if (clk_id == NULL)
- return NULL;
-
- snprintf(devname, MAX_DEVID_LENGTH, "tegra_%s", module);
- info->clk = clk_get_sys(devname, clk_id);
- if (IS_ERR_OR_NULL(info->clk)) {
- clk_id = NULL;
- return NULL;
+ if (mod->desc->idle)
+ mod->desc->idle(mod);
}
-
- info->default_rate = clk_round_rate(info->clk, info->default_rate);
- if (info->default_rate < 0) {
- pr_err("%s: can't get maximum rate for %s\n",
- __func__, clk_id);
- clk_id = NULL;
- }
-
- return clk_id;
}
-/* 3D power gating disabled as it causes syncpt hangs */
-static bool _3d_powergating_disabled(void)
+int nvhost_module_get_rate(struct nvhost_master *host,
+ struct nvhost_module *mod, unsigned long *rate,
+ int index)
{
- return 1;
+ int ret = -EINVAL;
+ if (host_acm_op(host).get_rate)
+ ret = host_acm_op(host).get_rate(mod, rate, index);
+ return ret;
}
-/* MPE power gating disabled as it causes syncpt hangs */
-static bool _mpe_powergating_disabled(void)
+int nvhost_module_set_rate(struct nvhost_master *host,
+ struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index)
{
- return 1;
-}
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
-int nvhost_module_get_rate(struct nvhost_module *mod, unsigned long *rate,
- int index)
-{
- struct clk *c;
-
- c = mod->clk[index].clk;
- if (IS_ERR_OR_NULL(c))
- return -EINVAL;
-
- nvhost_module_busy(mod);
- *rate = clk_get_rate(c);
- nvhost_module_idle(mod);
- return 0;
+ int ret = -EINVAL;
+ if (host_acm_op(host).set_rate)
+ ret = host_acm_op(host).set_rate(mod, priv, rate, index);
+ return ret;
}
-int nvhost_module_update_rate(struct nvhost_module *mod, int index)
+int nvhost_module_add_client(struct nvhost_master *host,
+ struct nvhost_module *mod, void *priv)
{
- unsigned long rate = 0;
- struct nvhost_module_client *m;
-
- if (!mod->clk[index].clk)
- return -EINVAL;
-
- list_for_each_entry(m, &mod->client_list, node) {
- rate = max(m->rate[index], rate);
- }
- if (!rate)
- rate = mod->clk[index].default_rate;
-
- clk_set_rate(mod->clk[index].clk, rate);
- return 0;
+ int ret = 0;
+ if (host_acm_op(host).add_client)
+ ret = host_acm_op(host).add_client(mod, priv);
+ return ret;
}
-int nvhost_module_set_rate(struct nvhost_module *mod, void *priv,
- unsigned long rate, int index)
+void nvhost_module_remove_client(struct nvhost_master *host,
+ struct nvhost_module *mod, void *priv)
{
- struct nvhost_module_client *m;
- int err;
-
- mutex_lock(&mod->lock);
- list_for_each_entry(m, &mod->client_list, node) {
- if (m->priv == priv) {
- rate = clk_round_rate(mod->clk[index].clk, rate);
- m->rate[index] = rate;
- break;
- }
- }
- err = nvhost_module_update_rate(mod, index);
- mutex_unlock(&mod->lock);
- return err;
+ if (host_acm_op(host).remove_client)
+ host_acm_op(host).remove_client(mod, priv);
}
-int nvhost_module_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].clk,
- mod->clk[i].default_rate);
- client->rate[i] = rate;
- }
- mutex_lock(&mod->lock);
- list_add_tail(&client->node, &mod->client_list);
- mutex_unlock(&mod->lock);
- return 0;
-}
-
-void nvhost_module_remove_client(struct nvhost_module *mod, void *priv)
-{
- int i;
- struct nvhost_module_client *m;
-
- mutex_lock(&mod->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++)
- nvhost_module_update_rate(mod, i);
- mutex_unlock(&mod->lock);
-}
-#else
-int nvhost_module_get_rate(struct nvhost_module *mod, unsigned long *rate,
- int index)
-{
- return 0;
-}
-
-int nvhost_module_set_rate(struct nvhost_module *mod, void *priv,
- unsigned long rate, int index)
-{
- return 0;
-}
-
-int nvhost_module_add_client(struct nvhost_module *mod, void *priv)
-{
- return 0;
-}
-
-void nvhost_module_remove_client(struct nvhost_module *mod, void *priv)
-{
-}
-#endif
-
int nvhost_module_init(struct nvhost_module *mod, const char *name,
- nvhost_modulef func, struct nvhost_module *parent,
+ const struct nvhost_moduledesc *desc,
+ struct nvhost_module *parent,
struct device *dev)
{
int i = 0;
@@ -604,67 +217,39 @@ int nvhost_module_init(struct nvhost_module *mod, const char *name,
mod->name = name;
INIT_LIST_HEAD(&mod->client_list);
- while (i < NVHOST_MODULE_MAX_CLOCKS) {
- if (get_module_clk(name, i, dev, &mod->clk[i]) == NULL)
- break;
-
- clk_enable(mod->clk[i].clk);
- clk_set_rate(mod->clk[i].clk, mod->clk[i].default_rate);
- clk_disable(mod->clk[i].clk);
+ while (desc->clocks[i].name && i < NVHOST_MODULE_MAX_CLOCKS) {
+ char devname[MAX_DEVID_LENGTH];
+ long rate = desc->clocks[i].default_rate;
+
+ snprintf(devname, MAX_DEVID_LENGTH, "tegra_%s", name);
+ mod->clk[i] = clk_get_sys(devname, desc->clocks[i].name);
+ BUG_ON(IS_ERR_OR_NULL(mod->clk[i]));
+
+ rate = clk_round_rate(mod->clk[i], rate);
+ clk_enable(mod->clk[i]);
+ clk_set_rate(mod->clk[i], rate);
+ clk_disable(mod->clk[i]);
i++;
}
-
mod->num_clks = i;
- mod->func = func;
+ mod->desc = desc;
mod->parent = parent;
- mod->can_powergate = false;
mod->powered = false;
- mod->powergate_id = -1;
- mod->powergate_id2 = -1;
- mod->powerdown_delay = ACM_POWERDOWN_HANDLER_DELAY_MSEC;
-
- if (strcmp(name, "gr2d") == 0)
- mod->powerdown_delay = 0;
- else if (strcmp(name, "gr3d") == 0) {
- mod->can_powergate = !_3d_powergating_disabled();
- if (!scale3d.init)
- scale3d_init(mod);
- mod->powergate_id = TEGRA_POWERGATE_3D;
- if (!mod->can_powergate)
- tegra_unpowergate_partition(mod->powergate_id);
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
- mod->powergate_id2 = TEGRA_POWERGATE_3D1;
- if (!mod->can_powergate)
- tegra_unpowergate_partition(mod->powergate_id2);
-#endif
- } else if (strcmp(name, "mpe") == 0) {
- mod->can_powergate = true;
- mod->powergate_id = TEGRA_POWERGATE_MPE;
- }
-
- if (mod->powergate_id == TEGRA_POWERGATE_MPE
- && _mpe_powergating_disabled()) {
- tegra_unpowergate_partition(mod->powergate_id);
- mod->powergate_id = -1;
- }
-
- if (mod->powergate_id == TEGRA_POWERGATE_3D
- && _3d_powergating_disabled()) {
- tegra_unpowergate_partition(mod->powergate_id);
- mod->powergate_id = -1;
-
-#ifdef CONFIG_ARCH_TEGRA_3x_SOC
- if (mod->powergate_id2 == TEGRA_POWERGATE_3D1) {
- tegra_unpowergate_partition(mod->powergate_id2);
- mod->powergate_id2 = -1;
- }
-#endif
- }
mutex_init(&mod->lock);
init_waitqueue_head(&mod->idle);
INIT_DELAYED_WORK(&mod->powerdown, powerdown_handler);
+ if (!desc->can_powergate) {
+ if (desc->powergate_ids[0] != -1)
+ tegra_unpowergate_partition(desc->powergate_ids[0]);
+ if (desc->powergate_ids[1] != -1)
+ tegra_unpowergate_partition(desc->powergate_ids[1]);
+ }
+
+ if (desc->init)
+ desc->init(dev, mod);
+
return 0;
}
@@ -685,19 +270,21 @@ static void debug_not_idle(struct nvhost_master *dev)
for (i = 0; i < dev->nb_channels; i++) {
struct nvhost_module *m = &dev->channels[i].mod;
if (m->name)
- printk("tegra_grhost: %s: refcnt %d\n",
+ dev_warn(&dev->pdev->dev, "tegra_grhost: %s: refcnt %d\n",
m->name, atomic_read(&m->refcount));
}
for (i = 0; i < dev->nb_mlocks; i++) {
int c = atomic_read(&dev->cpuaccess.lock_counts[i]);
if (c) {
- printk("tegra_grhost: lock id %d: refcnt %d\n", i, c);
+ dev_warn(&dev->pdev->dev,
+ "tegra_grhost: lock id %d: refcnt %d\n",
+ i, c);
lock_released = false;
}
}
if (lock_released)
- printk("tegra_grhost: all locks released\n");
+ dev_dbg(&dev->pdev->dev, "tegra_grhost: all locks released\n");
}
void nvhost_module_suspend(struct nvhost_module *mod, bool system_suspend)
@@ -719,54 +306,28 @@ void nvhost_module_suspend(struct nvhost_module *mod, bool system_suspend)
nvhost_debug_dump(dev);
if (system_suspend)
- printk("tegra_grhost: entered idle\n");
+ dev_dbg(&dev->pdev->dev, "tegra_grhost: entered idle\n");
flush_delayed_work(&mod->powerdown);
- cancel_work_sync(&scale3d.work);
if (system_suspend)
- printk("tegra_grhost: flushed delayed work\n");
+ dev_dbg(&dev->pdev->dev, "tegra_grhost: flushed delayed work\n");
+
+ if (mod->desc->suspend)
+ mod->desc->suspend(mod);
+
BUG_ON(mod->powered);
}
-void nvhost_module_deinit(struct nvhost_module *mod)
+void nvhost_module_deinit(struct device *dev, struct nvhost_module *mod)
{
int i;
+ if (mod->desc->deinit)
+ mod->desc->deinit(dev, mod);
+
nvhost_module_suspend(mod, false);
for (i = 0; i < mod->num_clks; i++)
- clk_put(mod->clk[i].clk);
+ clk_put(mod->clk[i]);
}
-
-/*
- * debugfs parameters to control 3d clock scaling
- */
-
-void nvhost_debug_scale_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
-}
diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h
index c726277ca00d..a31219eef97e 100644
--- a/drivers/video/tegra/host/nvhost_acm.h
+++ b/drivers/video/tegra/host/nvhost_acm.h
@@ -29,60 +29,72 @@
#include <linux/clk.h>
#define NVHOST_MODULE_MAX_CLOCKS 3
-
+#define NVHOST_MODULE_MAX_POWERGATE_IDS 2
struct nvhost_module;
+struct nvhost_master;
-enum nvhost_power_action {
- NVHOST_POWER_ACTION_OFF,
- NVHOST_POWER_ACTION_ON,
+struct nvhost_moduledesc_clock {
+ char *name;
+ long default_rate;
};
-typedef void (*nvhost_modulef)(struct nvhost_module *mod, enum nvhost_power_action action);
+#define NVHOST_MODULE_NO_POWERGATING .powergate_ids = {-1, -1}
+#define NVHOST_DEFAULT_POWERDOWN_DELAY .powerdown_delay = 25
-struct nvhost_module_client {
- struct list_head node;
- unsigned long rate[NVHOST_MODULE_MAX_CLOCKS];
- void *priv;
-};
+struct nvhost_moduledesc {
+ void (*prepare_poweroff)(struct nvhost_module *mod);
+ void (*finalize_poweron)(struct nvhost_module *mod);
+ void (*busy)(struct nvhost_module *);
+ void (*idle)(struct nvhost_module *);
+ void (*suspend)(struct nvhost_module *);
+ void (*init)(struct device *dev, struct nvhost_module *);
+ void (*deinit)(struct device *dev, struct nvhost_module *);
-struct nvhost_module_clock_info {
- struct clk *clk;
- unsigned long default_rate;
+ int powergate_ids[NVHOST_MODULE_MAX_POWERGATE_IDS];
+ bool can_powergate;
+ int powerdown_delay;
+ struct nvhost_moduledesc_clock clocks[NVHOST_MODULE_MAX_CLOCKS];
};
struct nvhost_module {
const char *name;
- nvhost_modulef func;
struct delayed_work powerdown;
- struct nvhost_module_clock_info clk[NVHOST_MODULE_MAX_CLOCKS];
int num_clks;
+ struct clk *clk[NVHOST_MODULE_MAX_CLOCKS];
struct mutex lock;
bool powered;
atomic_t refcount;
wait_queue_head_t idle;
struct nvhost_module *parent;
- bool can_powergate;
- int powergate_id;
- int powergate_id2;
- int powerdown_delay;
+ const struct nvhost_moduledesc *desc;
struct list_head client_list;
};
int nvhost_module_init(struct nvhost_module *mod, const char *name,
- nvhost_modulef func, struct nvhost_module *parent,
+ const struct nvhost_moduledesc *desc,
+ struct nvhost_module *parent,
struct device *dev);
-void nvhost_module_deinit(struct nvhost_module *mod);
+void nvhost_module_deinit(struct device *dev, struct nvhost_module *mod);
void nvhost_module_suspend(struct nvhost_module *mod, bool system_suspend);
-void nvhost_module_reset(struct nvhost_module *mod);
+void nvhost_module_reset(struct device *dev, struct nvhost_module *mod);
void nvhost_module_busy(struct nvhost_module *mod);
void nvhost_module_idle_mult(struct nvhost_module *mod, int refs);
-int nvhost_module_add_client(struct nvhost_module *mod, void *priv);
-void nvhost_module_remove_client(struct nvhost_module *mod, void *priv);
-int nvhost_module_get_rate(struct nvhost_module *mod, unsigned long *rate,
- int index);
-int nvhost_module_set_rate(struct nvhost_module *mod, void *priv,
- unsigned long rate, int index);
+int nvhost_module_add_client(struct nvhost_master *host,
+ struct nvhost_module *mod,
+ void *priv);
+void nvhost_module_remove_client(struct nvhost_master *host,
+ struct nvhost_module *mod,
+ void *priv);
+int nvhost_module_get_rate(struct nvhost_master *host,
+ struct nvhost_module *mod,
+ unsigned long *rate,
+ int index);
+int nvhost_module_set_rate(struct nvhost_master *host,
+ struct nvhost_module *mod, void *priv,
+ unsigned long rate, int index);
+
+#define host_acm_op(host) (host->op.acm)
static inline bool nvhost_module_powered(struct nvhost_module *mod)
{
@@ -94,10 +106,5 @@ static inline void nvhost_module_idle(struct nvhost_module *mod)
nvhost_module_idle_mult(mod, 1);
}
-/*
- * call when performing submit to notify scaling mechanism that 3d module is
- * in use
- */
-void module3d_notify_busy(void);
#endif
diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c
index d533ef3cd737..48f018cca4b2 100644
--- a/drivers/video/tegra/host/nvhost_channel.c
+++ b/drivers/video/tegra/host/nvhost_channel.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Channel
*
- * Copyright (c) 2010, NVIDIA Corporation.
+ * 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
@@ -33,12 +33,14 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch)
mutex_lock(&ch->reflock);
if (ch->refcount == 0) {
err = nvhost_module_init(&ch->mod, ch->desc->name,
- ch->desc->power, &ch->dev->mod,
+ &ch->desc->module,
+ &ch->dev->mod,
&ch->dev->pdev->dev);
if (!err) {
err = nvhost_cdma_init(&ch->cdma);
if (err)
- nvhost_module_deinit(&ch->mod);
+ nvhost_module_deinit(&ch->dev->pdev->dev,
+ &ch->mod);
}
} else if (ch->desc->exclusive) {
err = -EBUSY;
@@ -72,7 +74,7 @@ void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx)
mutex_lock(&ch->reflock);
if (ch->refcount == 1) {
- nvhost_module_deinit(&ch->mod);
+ nvhost_module_deinit(&ch->dev->pdev->dev, &ch->mod);
/* cdma may already be stopped, that's ok */
channel_cdma_op(ch).stop(&ch->cdma);
diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h
index c35c6d0a010c..e4dc4e2d79e8 100644
--- a/drivers/video/tegra/host/nvhost_channel.h
+++ b/drivers/video/tegra/host/nvhost_channel.h
@@ -33,19 +33,20 @@
#define NVHOST_MAX_WAIT_CHECKS 256
#define NVHOST_MAX_GATHERS 512
#define NVHOST_MAX_HANDLES 1280
+#define NVHOST_MAX_POWERGATE_IDS 2
struct nvhost_master;
struct nvhost_waitchk;
struct nvhost_channeldesc {
const char *name;
- nvhost_modulef power;
u32 syncpts;
u32 waitbases;
u32 modulemutexes;
u32 class;
bool exclusive;
bool keepalive;
+ struct nvhost_moduledesc module;
};
struct nvhost_channel {
diff --git a/drivers/video/tegra/host/nvhost_scale.h b/drivers/video/tegra/host/nvhost_scale.h
deleted file mode 100644
index 591a5fc04ed9..000000000000
--- a/drivers/video/tegra/host/nvhost_scale.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * drivers/video/tegra/host/nvhost_scale.h
- *
- * Tegra Graphics Host 3D clock scaling
- *
- * Copyright (c) 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_SCALE_H
-#define __NVHOST_SCALE_H
-
-#include <mach/clk.h>
-#include <linux/hrtimer.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-
-/*
- * 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 {
- spinlock_t lock; /* lock for timestamps etc */
- struct mutex set_lock; /* lock for clock setting */
- 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;
-};
-
-/* reset 3d module load counters, called on resume */
-void scale3d_reset(void);
-
-int scale3d_is_enabled(void);
-void scale3d_enable(int enable);
-
-#endif /* __NVHOST_SCALE_H */
diff --git a/drivers/video/tegra/host/t20/cdma_t20.c b/drivers/video/tegra/host/t20/cdma_t20.c
index 9413455d4b3b..633d4e700f8c 100644
--- a/drivers/video/tegra/host/t20/cdma_t20.c
+++ b/drivers/video/tegra/host/t20/cdma_t20.c
@@ -137,7 +137,6 @@ static void t20_push_buffer_push_to(struct push_buffer *pb,
pb->nvmap[cur/8].client = client;
pb->nvmap[cur/8].handle = handle;
pb->cur = (cur + 8) & (PUSH_BUFFER_SIZE - 1);
- /* printk("push_to_push_buffer: op1=%08x; op2=%08x; cur=%x\n", op1, op2, pb->cur); */
}
/**
@@ -578,7 +577,7 @@ void t20_cdma_timeout_teardown_begin(struct nvhost_cdma *cdma)
ch->aperture + HOST1X_CHANNEL_DMACTRL);
writel(BIT(ch->chid), dev->sync_aperture + HOST1X_SYNC_CH_TEARDOWN);
- nvhost_module_reset(&ch->mod);
+ nvhost_module_reset(&dev->pdev->dev, &ch->mod);
cdma->running = false;
cdma->torndown = true;
diff --git a/drivers/video/tegra/host/t20/channel_t20.c b/drivers/video/tegra/host/t20/channel_t20.c
index 386682b6f0ed..2617c9bd46da 100644
--- a/drivers/video/tegra/host/t20/channel_t20.c
+++ b/drivers/video/tegra/host/t20/channel_t20.c
@@ -24,12 +24,13 @@
#include "../dev.h"
#include "../nvhost_hwctx.h"
#include <trace/events/nvhost.h>
+#include <mach/powergate.h>
#include "hardware_t20.h"
#include "syncpt_t20.h"
#include "../dev.h"
#include "3dctx_t20.h"
-#include "../t30/3dctx_t30.h"
+#include "../3dctx_common.h"
#define NVHOST_NUMCHANNELS (NV_HOST1X_CHANNELS - 1)
#define NVHOST_CHANNEL_BASE 0
@@ -45,9 +46,6 @@
#define NVMODMUTEX_DSI (9)
#define NV_FIFO_READ_TIMEOUT 200000
-static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action);
-
-
const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
{
/* channel 0 */
@@ -57,6 +55,10 @@ const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
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 */
@@ -65,7 +67,12 @@ const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
.waitbases = BIT(NVWAITBASE_3D),
.modulemutexes = BIT(NVMODMUTEX_3D),
.class = NV_GRAPHICS_3D_CLASS_ID,
- .power = power_3d,
+ .module = {
+ .prepare_poweroff = nvhost_3dctx_prepare_power_off,
+ .clocks = {{"gr3d", UINT_MAX}, {"emc", UINT_MAX}, {} },
+ .powergate_ids = {TEGRA_POWERGATE_3D, -1},
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
},
{
/* channel 2 */
@@ -74,11 +81,22 @@ const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
.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", UINT_MAX} ,
+ {"epp", UINT_MAX} ,
+ {"emc", UINT_MAX} },
+ NVHOST_MODULE_NO_POWERGATING,
+ .powerdown_delay = 0,
+ }
},
{
/* channel 3 */
.name = "isp",
.syncpts = 0,
+ .module = {
+ NVHOST_MODULE_NO_POWERGATING,
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ },
},
{
/* channel 4 */
@@ -89,6 +107,10 @@ const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
BIT(NVSYNCPT_VI_ISP_4),
.modulemutexes = BIT(NVMODMUTEX_VI),
.exclusive = true,
+ .module = {
+ NVHOST_MODULE_NO_POWERGATING,
+ NVHOST_DEFAULT_POWERDOWN_DELAY,
+ }
},
{
/* channel 5 */
@@ -99,12 +121,21 @@ const struct nvhost_channeldesc nvhost_t20_channelmap[] = {
.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,
+ },
}};
static inline void __iomem *t20_channel_aperture(void __iomem *p, int ndx)
@@ -164,8 +195,8 @@ static int t20_channel_submit(struct nvhost_channel *channel,
/* keep module powered */
nvhost_module_busy(&channel->mod);
- if (strcmp(channel->mod.name, "gr3d") == 0)
- module3d_notify_busy();
+ if (channel->mod.desc->busy)
+ channel->mod.desc->busy(&channel->mod);
/* before error checks, return current max */
*syncpt_value = nvhost_syncpt_read_max(sp, syncpt_id);
@@ -317,63 +348,6 @@ static int t20_channel_submit(struct nvhost_channel *channel,
return 0;
}
-static void power_3d(struct nvhost_module *mod, enum nvhost_power_action action)
-{
- struct nvhost_channel *ch = container_of(mod, struct nvhost_channel, mod);
- struct nvhost_hwctx *hwctx_to_save;
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
- u32 syncpt_incrs, syncpt_val;
- int err;
- void *ref;
-
- if (action != NVHOST_POWER_ACTION_OFF)
- return;
-
- mutex_lock(&ch->submitlock);
- hwctx_to_save = ch->cur_ctx;
- if (!hwctx_to_save) {
- mutex_unlock(&ch->submitlock);
- return;
- }
-
- if (strcmp(mod->name, "gr3d") == 0)
- module3d_notify_busy();
-
- err = nvhost_cdma_begin(&ch->cdma, hwctx_to_save->timeout);
- if (err) {
- mutex_unlock(&ch->submitlock);
- return;
- }
-
- hwctx_to_save->valid = true;
- ch->ctxhandler.get(hwctx_to_save);
- ch->cur_ctx = NULL;
-
- syncpt_incrs = hwctx_to_save->save_incrs;
- syncpt_val = nvhost_syncpt_incr_max(&ch->dev->syncpt,
- NVSYNCPT_3D, syncpt_incrs);
-
- ch->ctxhandler.save_push(&ch->cdma, hwctx_to_save);
- nvhost_cdma_end(&ch->cdma, ch->dev->nvmap, NVSYNCPT_3D, syncpt_val,
- NULL, 0, hwctx_to_save->timeout);
-
- nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D,
- syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh,
- NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save, NULL);
-
- nvhost_intr_add_action(&ch->dev->intr, NVSYNCPT_3D, syncpt_val,
- NVHOST_INTR_ACTION_WAKEUP, &wq, &ref);
- wait_event(wq,
- nvhost_syncpt_min_cmp(&ch->dev->syncpt,
- NVSYNCPT_3D, syncpt_val));
-
- nvhost_intr_put_ref(&ch->dev->intr, ref);
-
- nvhost_cdma_update(&ch->cdma);
-
- mutex_unlock(&ch->submitlock);
-}
-
static int t20_channel_read_3d_reg(
struct nvhost_channel *channel,
struct nvhost_hwctx *hwctx,
diff --git a/drivers/video/tegra/host/t20/t20.h b/drivers/video/tegra/host/t20/t20.h
index 8a8426c740d1..406841092f01 100644
--- a/drivers/video/tegra/host/t20/t20.h
+++ b/drivers/video/tegra/host/t20/t20.h
@@ -22,6 +22,8 @@
#ifndef _NVHOST_T20_H_
#define _NVHOST_T20_H_
+struct nvhost_master;
+
int nvhost_init_t20_channel_support(struct nvhost_master *);
int nvhost_init_t20_cdma_support(struct nvhost_master *);
int nvhost_init_t20_debug_support(struct nvhost_master *);
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_ */