diff options
author | Prashant Gaikwad <pgaikwad@nvidia.com> | 2011-08-02 21:03:59 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-08-08 11:24:43 -0700 |
commit | 621b21120db2d8894a564701637fe0a95aae6442 (patch) | |
tree | f75187ccad820cf1d57e20745f7e7b6b4ff87354 | |
parent | 5728079a265ce38e782a9766a6ee6e0cfbe45fef (diff) |
video: tegra: host: Add ioctl to set/get clk rate
Host modules are initialized to max rate. Not all use cases
require clocks at max rate, which increases the power consumption.
Modules from user space can request for the lower clk rate
using this ioctl.
Bug 850467
Change-Id: I1c7a8dfd159460e7c5a27813e3a08a992a20c132
Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com>
Reviewed-on: http://git-master/r/44579
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/nvhost.h | 10 | ||||
-rw-r--r-- | drivers/video/tegra/host/dev.c | 22 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_acm.c | 105 | ||||
-rw-r--r-- | drivers/video/tegra/host/nvhost_acm.h | 13 |
4 files changed, 149 insertions, 1 deletions
diff --git a/arch/arm/mach-tegra/include/mach/nvhost.h b/arch/arm/mach-tegra/include/mach/nvhost.h index 01cb1cff390e..8d7031791eab 100644 --- a/arch/arm/mach-tegra/include/mach/nvhost.h +++ b/arch/arm/mach-tegra/include/mach/nvhost.h @@ -133,6 +133,10 @@ struct nvhost_read_3d_reg_args { __u32 value; }; +struct nvhost_clk_rate_args { + __u64 rate; +}; + #define NVHOST_IOCTL_CHANNEL_FLUSH \ _IOR(NVHOST_IOCTL_MAGIC, 1, struct nvhost_get_param_args) #define NVHOST_IOCTL_CHANNEL_GET_SYNCPOINTS \ @@ -149,8 +153,12 @@ struct nvhost_read_3d_reg_args { _IOW(NVHOST_IOCTL_MAGIC, 7, struct nvhost_submit_hdr_ext) #define NVHOST_IOCTL_CHANNEL_READ_3D_REG \ _IOWR(NVHOST_IOCTL_MAGIC, 8, struct nvhost_read_3d_reg_args) +#define NVHOST_IOCTL_CHANNEL_GET_CLK_RATE \ + _IOR(NVHOST_IOCTL_MAGIC, 9, struct nvhost_clk_rate_args) +#define NVHOST_IOCTL_CHANNEL_SET_CLK_RATE \ + _IOW(NVHOST_IOCTL_MAGIC, 10, struct nvhost_clk_rate_args) #define NVHOST_IOCTL_CHANNEL_LAST \ - _IOC_NR(NVHOST_IOCTL_CHANNEL_READ_3D_REG) + _IOC_NR(NVHOST_IOCTL_CHANNEL_SET_CLK_RATE) #define NVHOST_IOCTL_CHANNEL_MAX_ARG_SIZE sizeof(struct nvhost_submit_hdr_ext) struct nvhost_ctrl_syncpt_read_args { diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index c9e478ab8412..41b3493ce59e 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -76,6 +76,7 @@ static int nvhost_channelrelease(struct inode *inode, struct file *filp) filp->private_data = NULL; + nvhost_module_remove_client(&priv->ch->mod, priv); nvhost_putchannel(priv->ch, priv->hwctx); if (priv->hwctx) @@ -111,6 +112,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); priv->gather_mem = nvmap_alloc(ch->dev->nvmap, sizeof(u32) * 2 * NVHOST_MAX_GATHERS, 32, NVMAP_HANDLE_CACHEABLE); @@ -422,6 +424,26 @@ static long nvhost_channelctl(struct file *filp, case NVHOST_IOCTL_CHANNEL_READ_3D_REG: err = nvhost_ioctl_channel_read_3d_reg(priv, (void *)buf); break; + case NVHOST_IOCTL_CHANNEL_GET_CLK_RATE: + { + unsigned long rate; + struct nvhost_clk_rate_args *arg = + (struct nvhost_clk_rate_args *)buf; + + err = nvhost_module_get_rate(&priv->ch->mod, &rate, 0); + if (err == 0) + arg->rate = rate; + break; + } + case NVHOST_IOCTL_CHANNEL_SET_CLK_RATE: + { + struct nvhost_clk_rate_args *arg = + (struct nvhost_clk_rate_args *)buf; + unsigned long rate = (unsigned long)arg->rate; + + err = nvhost_module_set_rate(&priv->ch->mod, priv, rate, 0); + break; + } default: err = -ENOTTY; break; diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c index 4d14dc1093dd..e477d5a6ad85 100644 --- a/drivers/video/tegra/host/nvhost_acm.c +++ b/drivers/video/tegra/host/nvhost_acm.c @@ -21,6 +21,7 @@ */ #include "dev.h" +#include <linux/slab.h> #include <linux/string.h> #include <linux/sched.h> #include <linux/err.h> @@ -128,6 +129,109 @@ static bool _3d_powergating_disabled(void) 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]; + if (IS_ERR_OR_NULL(c)) + return -EINVAL; + + *rate = clk_get_rate(c); + return 0; +} + +int nvhost_module_update_rate(struct nvhost_module *mod, int index) +{ + unsigned long rate = 0; + struct nvhost_module_client *m; + + list_for_each_entry(m, &mod->client_list, node) { + rate = max(m->rate[index], rate); + } + if (!mod->clk[index]) + return -EINVAL; + clk_set_rate(mod->clk[index], rate); + return 0; +} + +int nvhost_module_set_rate(struct nvhost_module *mod, void *priv, + unsigned long rate, int index) +{ + struct nvhost_module_client *m; + + 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; + } + } + return nvhost_module_update_rate(mod, index); +} + +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], 0); + client->rate[i] = rate; + } + list_add_tail(&client->node, &mod->client_list); + return 0; +} + +void nvhost_module_remove_client(struct nvhost_module *mod, void *priv) +{ + int i; + struct nvhost_module_client *m; + + 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); +} +#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, struct device *dev) @@ -136,6 +240,7 @@ 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) { long rate; mod->clk[i] = clk_get(dev, get_module_clk_id(name, i)); diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h index 9736eb64c633..2efdadf29723 100644 --- a/drivers/video/tegra/host/nvhost_acm.h +++ b/drivers/video/tegra/host/nvhost_acm.h @@ -39,6 +39,12 @@ enum nvhost_power_action { typedef void (*nvhost_modulef)(struct nvhost_module *mod, enum nvhost_power_action action); +struct nvhost_module_client { + struct list_head node; + unsigned long rate[NVHOST_MODULE_MAX_CLOCKS]; + void *priv; +}; + struct nvhost_module { const char *name; nvhost_modulef func; @@ -53,6 +59,7 @@ struct nvhost_module { int powergate_id; int powergate_id2; int powerdown_delay; + struct list_head client_list; }; int nvhost_module_init(struct nvhost_module *mod, const char *name, @@ -63,6 +70,12 @@ void nvhost_module_suspend(struct nvhost_module *mod, bool system_suspend); 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); static inline bool nvhost_module_powered(struct nvhost_module *mod) { |