diff options
author | Terje Bergstrom <tbergstrom@nvidia.com> | 2011-09-01 08:05:10 +0300 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-09-16 14:58:07 -0700 |
commit | ebea06768d9c9d351a7d1c8dc6499c97f2f5002d (patch) | |
tree | 4c2c95e48ba092c7df92ef81b078de106a15bce2 /drivers | |
parent | f837e5b34d311b13a52996b68b27b0207c15bd79 (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')
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_ */ |